Scala、Specs2 和共享状态
Scala, Specs2 and shared state
我正在编写一些 Specs2 规范;看起来像:
class ComponentSpecification extends Specification with Mockito {
private val dependency = mock[Dependency]
private val subject = new Component(..)
"methodOne" should {
"handle happy path" in {
val result = subject.methodOne("Param1", 42)
result must ...
there was one(dependency).something()
}
"deal with border case" in {
val result = subject.methodOne("", -1)
result must ...
there was one(dependency).something()
}
}
}
但是,这些测试失败了,因为 mock[Dependency]
是共享的。
- 一个解决方案是让它们按顺序排列并在每次测试前重置模拟,但这看起来很奇怪,正如文档中写的关于 "Parallel by default":
it encourages to write independent examples when the result of a given example should not be influenced by others
另一种方法是将 val
移动到测试本身。但是虽然我应该能够减少重复,但它仍然看起来像一个奇怪的结构。看起来 subject
是有状态的,但它不应该。
我也可以通过 there was atLestOne(dependency).something()
验证来尝试使用不太严格的方法,但是:
- 这不会验证在这个特定的测试用例中调用了该方法,
- 参数捕获和验证很痛苦。
所以我的问题是:
如何在 mock 上创建具有详细验证的可读测试。
非常感谢。
Scopes 可以像这样为每个测试提供新鲜状态
class ComponentSpecification extends mutable.Specification with Mockito {
trait FooScope extends Scope {
val dependency = mock[Dependency]
val subject = new Component(dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val result = subject.methodOne("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val result = subject.methodOne("", -1)
there was one(dependency).something()
}
}
}
无需在每次测试前重置模拟。
我打算接受@Mario Galic 的回答。然而,关于@Eric(Specs2 的作者)的评论,我以一种按预期创建上下文但删除重复的方法结束。通过使用模式匹配,我提取了有趣的部分:
class ComponentSpecification extends mutable.Specification with Mockito {
def givenOneCallToMethodOneWithDependency(s:String, i:Int):(Result, Dependency) = {
val dependency = mock[Dependency]
val subject = new Component(dependency)
val result = subject.methodOne(s, i)
(result, dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("", -1)
there was one(dependency).something()
}
}
}
我正在编写一些 Specs2 规范;看起来像:
class ComponentSpecification extends Specification with Mockito {
private val dependency = mock[Dependency]
private val subject = new Component(..)
"methodOne" should {
"handle happy path" in {
val result = subject.methodOne("Param1", 42)
result must ...
there was one(dependency).something()
}
"deal with border case" in {
val result = subject.methodOne("", -1)
result must ...
there was one(dependency).something()
}
}
}
但是,这些测试失败了,因为 mock[Dependency]
是共享的。
- 一个解决方案是让它们按顺序排列并在每次测试前重置模拟,但这看起来很奇怪,正如文档中写的关于 "Parallel by default":
it encourages to write independent examples when the result of a given example should not be influenced by others
另一种方法是将
val
移动到测试本身。但是虽然我应该能够减少重复,但它仍然看起来像一个奇怪的结构。看起来subject
是有状态的,但它不应该。我也可以通过
there was atLestOne(dependency).something()
验证来尝试使用不太严格的方法,但是:- 这不会验证在这个特定的测试用例中调用了该方法,
- 参数捕获和验证很痛苦。
所以我的问题是:
如何在 mock 上创建具有详细验证的可读测试。
非常感谢。
Scopes 可以像这样为每个测试提供新鲜状态
class ComponentSpecification extends mutable.Specification with Mockito {
trait FooScope extends Scope {
val dependency = mock[Dependency]
val subject = new Component(dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val result = subject.methodOne("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val result = subject.methodOne("", -1)
there was one(dependency).something()
}
}
}
无需在每次测试前重置模拟。
我打算接受@Mario Galic 的回答。然而,关于@Eric(Specs2 的作者)的评论,我以一种按预期创建上下文但删除重复的方法结束。通过使用模式匹配,我提取了有趣的部分:
class ComponentSpecification extends mutable.Specification with Mockito {
def givenOneCallToMethodOneWithDependency(s:String, i:Int):(Result, Dependency) = {
val dependency = mock[Dependency]
val subject = new Component(dependency)
val result = subject.methodOne(s, i)
(result, dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("", -1)
there was one(dependency).something()
}
}
}