3

私の最初の質問である質問には回答がありましたが、私が抱えている別の問題が明らかになりました。これがシナリオです。

サンプルコード (前の質問から拡張)

モデル:

case class User (first: String, last: String, enabled: Boolean)

コンポーネント定義:

trait DataProviderComponent {
  def find[T: ClassTag](id: Int): Try[T]
  def update[T](data: T): Try[T]
}

具体的なコンポーネントの実装の 1 つ (更新された実装):

class DbProvider extends DataProviderComponent {
  override def find[T: ClassTag](id: Int): Try[T] = {
    Try {
      val gson = new Gson()
      val testJson = """{"first": "doe", "last": "jane", "enabled": false}"""

      gson.fromJson(testJson, implicitly[ClassTag[T]].runtimeClass).asInstanceOf[T]
    }
  }

  override def update[T](data: T): Try[T] = ???
}

システム内のどこかでコンポーネント impl が暗黙的に使用されています:

implicit val provider = new DbProvider()

class UserRepsitory(implicit provider: DataProviderComponent) {
  def userEnabled(id: Int): Boolean = {
    val user = provider.find[User](id)
    user.isSuccess && user.get.enabled
  }
}

単体テスト 1、リポジトリ テストを分離するためにプロバイダーをモックアウトしようとしています。これは機能しません。テストの実行時に次の execption がスローされます。ClassTag を使用しない別のサンプルを作成すると、正常に動作するため、ClassTag の使用が原因であると思われます。

org.scalamock.MockFunction2 は org.scalamock.MockFunction1 にキャストできません java.lang.ClassCastException: org.scalamock.MockFunction2 は org.scalamock.MockFunction1 にキャストできません

class UserRepository$Test1 extends FunSuite with Matchers with MockFactory {
  test("invalid data request should return false") {
    implicit val mockProvider: DataProviderComponent = mock[DataProviderComponent]
    (mockProvider.find[User] _).expects(13).returns(Failure[User](new Exception("Failed!")))

    val repo = new UserRepsitory()
    repo.userEnabled(13) should be (false)
  }
}

Unit Test2 は機能しますが、保守が難しく、より多くのコードが必要です。

class UserRepository$Test2 extends FunSuite with Matchers with MockFactory {
  test("invalid data request should return false") {
    class FakeProvider extends DataProviderComponent {
      override def find[T:ClassTag](id: Int): Try[T] = Failure[T](new Exception("Failed!"))
      override def update[T](data: T): Try[T] = ???
    }

    implicit val provider = new FakeProvider()
    val repo = new UserRepsitory()
    repo.userEnabled(13) should be (false)
  }
}

Unit Test3 は機能しますが、ClassTag の実装をテストするためだけに使用されます。

class UserRepository$Test3 extends FunSuite with Matchers with MockFactory {
  test("prove sut works") {
    implicit val provider = new DbProvider()
    val repo = new UserRepsitory()
    val user = repo.userEnabled(13)
    println(user.toString)
  }
}

ClassTag を間違って使用していますか、それともモックが適切にモックできませんか?

4

1 に答える 1

4

これは次のようなものです: ScalaMock ユーザー ガイド: 暗黙的なパラメーターを使用したメソッドのモック- 暗黙的ClassTagなパラメーターがあるため、Scala コンパイラーfind[T](id:Int)(m: ClassTag[T])に変換する必要があることを納得させる必要があります。MockFunction2

次のコードは ScalaMock 3.2 で動作します。

package com.paulbutcher.test.mock

import org.scalamock.scalatest.MockFactory
import org.scalatest.{ FlatSpec, ShouldMatchers }

import scala.reflect.ClassTag
import scala.util.{ Failure, Try }

case class User(first: String, last: String, enabled: Boolean)

trait DataProviderComponent {
  def find[T: ClassTag](id: Int): Try[T]
  def update[T](data: T): Try[T]
}

class UserRepsitory(implicit provider: DataProviderComponent) {
  def userEnabled(id: Int): Boolean = {
    val user = provider.find[User](id)
    user.isSuccess && user.get.enabled
  }
}

class ClassTagTest extends FlatSpec with ShouldMatchers with MockFactory {
  behavior of "ScalaMock"

  it should "handle mocking methods with class tags" in {
    implicit val mockProvider: DataProviderComponent = mock[DataProviderComponent]
    (mockProvider.find[User](_: Int)(_: ClassTag[User])).expects(13, *).returns(Failure[User](new Exception("Failed!")))

    val repo = new UserRepsitory()
    repo.userEnabled(13) should be(false)
  }
}
于 2014-11-21T07:06:06.013 に答える