我一直在试图采取蛋糕模式,但我有适应这种编程风格,尤其是在单元测试都在关注困难。
让我们假设我有以下业务对象:
trait Vet {
def vaccinate(pet: Pet)
}
trait PetStore { this: Vet =>
def sell(pet: Pet) {
vaccinate(pet)
// do some other stuff
}
}
现在,我想测试的PetStore而从兽医嘲笑的职能。 如果我是用成分,我创建一个模拟[兽医],并把它传递给PetStore的构造器,那么编程模拟像我们在Java世界中做。 但是,我找不到人如何与蛋糕的图案做任何引用。
一个可能的解决方案是根据预期的使用情况,以实现对每个测试用例接种(),但是这则不允许我以验证嘲笑被正确调用,不允许我使用的匹配,等等。
所以 - 如何用蛋糕图案带有模拟对象的人吗?
我开始用蛋糕图案后,我读了这篇博客文章: 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
这是一个很好的问题。 我们得出的结论是无法做到的,至少不太我们已经习惯了同样的方式。 它可以使用存根,而不是嘲笑和蛋糕明智混合存根。 但是,这是不是用嘲弄更多的工作。
我们有两个斯卡拉队和一队采用的蛋糕图案,使用存根而不是嘲笑,而其他球队坚持类和依赖注入。 现在,我都试过,我宁愿用DI,由于它是简单的测试嘲笑。 按理说简单阅读了。
我已经找到一种方法来使用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
尽管这是一个老问题,我将我的未来的读者回答。 我相信这SO后- 如何使用与蛋糕模式模拟考试 -问和答案同样的事情。
我成功地跟着弗拉基米尔·Matveev给出的答案(这是在写作的时候顶端回答