【问题标题】:Scala, Specs2 and shared stateScala、Specs2 和共享状态
【发布时间】:2019-10-07 12:15:45
【问题描述】:

我正在写一些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] 是共享的。

  • 一种解决方案是让它们按顺序排列并在每次测试之前重置模拟,但这看起来很奇怪,并且在文档中关于“默认情况下并行”中所写:

当给定示例的结果不应受到其他人的影响时,它鼓励编写独立示例

  • 另一个是将val 移动到测试本身。但是,虽然我应该能够减少重复,但它仍然看起来像一个奇怪的结构。看起来subject 是有状态的,但它不应该。

  • 我也可以尝试使用不那么严格的方法,通过there was atLestOne(dependency).something() 进行验证,但是:

    1. 这并不能验证该方法是否在此特定测试用例中被调用,并且
    2. 参数捕获和验证很痛苦。

所以我的问题是:

如何在模拟上创建具有详细验证的可读测试。

非常感谢。

【问题讨论】:

    标签: scala testing specs2


    【解决方案1】:

    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()
        }
      }
    }
    

    无需在每次测试前重置模拟。

    【讨论】:

    • 谢谢,我正在使用返回 Tuple[Subject, Dependency] 的私有方法进行等效处理,但这更简洁。我也落在isolated 关键字上,你有反对或支持etorreborre.github.io/specs2/guide/SPECS2-4.3.4/… 的论点而不是_scopes _?
    • 我会避免使用isolated,因为我对可变规范的实现不是很有信心。它需要做各种扭曲才能到达那里。另请注意,Scope 依赖于 DelayedInit 功能,不能保证保留在 Scala 3 中。因此,如果您想安全使用:每个示例中的 val dependency = mock[Dependency]; val subject = new Component (..)...
    【解决方案2】:

    我打算接受@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()
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2015-07-03
      相关资源
      最近更新 更多