斯卡拉:嘲讽和蛋糕模式(Scala: Mocking and the Cake Pattern)

2019-09-01 08:54发布

我一直在试图采取蛋糕模式,但我有适应这种编程风格,尤其是在单元测试都在关注困难。

让我们假设我有以下业务对象:

trait Vet {
  def vaccinate(pet: Pet)
}

trait PetStore { this: Vet =>
  def sell(pet: Pet) {
    vaccinate(pet)
    // do some other stuff
  }
}

现在,我想测试的PetStore而从兽医嘲笑的职能。 如果我是用成分,我创建一个模拟[兽医],并把它传递给PetStore的构造器,那么编程模拟像我们在Java世界中做。 但是,我找不到人如何与蛋糕的图案做任何引用。

一个可能的解决方案是根据预期的使用情况,以实现对每个测试用例接种(),但是这则不允许我以验证嘲笑被正确调用,不允许我使用的匹配,等等。

所以 - 如何用蛋糕图案带有模拟对象的人吗?

Answer 1:

我开始用蛋糕图案后,我读了这篇博客文章: https://github.com/precog/staticsite/blob/master/contents/blog/Existential-Types-FTW/index.md的方法是从最蛋糕模式不同在存在类型的职位用于自种的来代替。

我一直在使用这种模式了数个月,它似乎进行得很顺利,我可以指定一个模拟时,我想。 它有更多的依赖注入给人的感觉,但它有你具有特质代码的所有优点。

使用存在的类型你的问题我bastardized版本将是这样的:

case class Pet(val name: String)
trait ConfigComponent {
  type Config
  def config: Config
}

trait Vet {
  def vaccinate(pet: Pet) = {println ("Vaccinate:" + pet)}
}

trait PetStoreConfig {
  val vet: Vet
}
trait PetStore extends ConfigComponent {

    type Config <: PetStoreConfig

    def sell(pet: Pet) {
      config.vet.vaccinate(pet)
      // do some other stuff
    }
}

你可以把它放在一起在您的应用程序

class MyApp extends PetStore with PetStoreConfig {

  type Config = MyApp
  def config = this  

  val vet = new Vet{}
  sell(new Pet("Fido"))

}

scala> new MyApp
Vaccinate:Pet(Fido)
res0: MyApp = MyApp@668dd96c

你可以通过创建VetLike的实例,也创建一个使用它你的PetStore测试VetLike的模拟单独测试组件。

//Test VetLike Behavior
scala> val vet = new Vet{}
scala> vet.vaccinate(new Pet("Fido"))
Vaccinate:Pet(Fido)


//Test Petstore Behavior

class VetMock extends Vet {
   override def vaccinate(pet: Pet) = println("MOCKED")
}

class PetStoreTest extends PetStore with PetStoreConfig {
   type Config = PetStoreTest
   def config = this

   val vet = new VetMock
   val fido = new Pet("Fido")
   sell(fido)
}

scala> new PetStoreTest
MOCKED


Answer 2:

这是一个很好的问题。 我们得出的结论是无法做到的,至少不太我们已经习惯了同样的方式。 它可以使用存根,而不是嘲笑和蛋糕明智混合存根。 但是,这是不是用嘲弄更多的工作。

我们有两个斯卡拉队和一队采用的蛋糕图案,使用存根而不是嘲笑,而其他球队坚持类和依赖注入。 现在,我都试过,我宁愿用DI,由于它是简单的测试嘲笑。 按理说简单阅读了。



Answer 3:

我已经找到一种方法来使用Scalamock与Scalatest进行单元测试“蛋糕模式”模块的目的。

起初,我有许多问题(包括这一个),但我相信了下面给出的解决方案是可接受的。 如果您有任何疑问,请让我知道。

这是我会怎么设计自己的例子:

trait VetModule {
  def vet: Vet
  trait Vet {
    def vaccinate(pet: Pet)
  }
}

trait PetStoreModule {
  self: VetModule =>
  def sell(pet: Pet)
}

trait PetStoreModuleImpl extends PetStoreModule {
  self: VetModule =>
  def sell(pet: Pet) {
    vet.vaccinate(pet)
    // do some other stuff
  }
}

这些测试然后定义如下:

class TestPetstore extends FlatSpec with ShouldMatchers with MockFactory {

  trait PetstoreBehavior extends PetStoreModule with VetModule {

    object MockWrapper {
      var vet: Vet = null
    }

    def fixture = {
      val v = mock[Vet]
      MockWrapper.vet = v
      v
    }

    def t1 {
      val vet = fixture
      val p = Pet("Fido")
      (vet.vaccinate _).expects(p)
      sell(p)
    }

    def vet: Vet = MockWrapper.vet
  }

  val somePetStoreImpl = new PetstoreBehavior with PetStoreModuleImpl
  "The PetStore" should "vaccinate an animal before selling" in somePetStoreImpl.t1
}

使用此设置,你有“缺点”,你必须调用val vet = fixture在每一个你写测试。 在另一方面,人们可以很容易地创建另一个测试的“执行”,例如,

val someOtherPetStoreImpl = new PetstoreBehavior with PetStoreModuleOtherImpl


Answer 4:

尽管这是一个老问题,我将我的未来的读者回答。 我相信这SO后- 如何使用与蛋糕模式模拟考试 -问和答案同样的事情。

我成功地跟着弗拉基米尔·Matveev给出的答案(这是在写作的时候顶端回答



文章来源: Scala: Mocking and the Cake Pattern
标签: scala mocking