我不了解Spock测试中的Mock,Stub和Spy之间的区别,而我在网上看到的教程也没有详细解释。

最佳答案

注意:在接下来的几段中,我将过分简化,甚至稍加捏造。有关更多详细信息,请参见Martin Fowler's website

模拟是一个虚拟类,代替了一个真实的类,为每个方法调用返回诸如null或0之类的东西。如果需要复杂类的虚拟实例,则可以使用模拟,否则将使用外部资源,例如网络连接,文件或数据库,或者可能使用许多其他对象。模拟的优点是您可以将测试中的类与系统的其余部分隔离。

存根也是一个虚拟类,为某些被测请求提供了一些更具体的,准备好的或预先记录的,重放的结果。您可以说存根是一种幻想。在Spock中,您将经常阅读有关存根方法的信息。

spy 是真实对象和存根之间的混合体,即它基本上是具有某些(不是全部)方法被存根方法所遮盖的真实对象。非存根方法只是被路由到原始对象。这样,对于“便宜”或琐碎的方法,您可以拥有原始行为;对于“昂贵”或复杂的方法,则可以具有假行为。

更新2017-02-06:实际上,用户mikhail的答案比我上面的原始答案更特定于Spock。因此,在Spock的范围内,他所描述的是正确的,但这并不能伪造我的一般答案:

  • 存根与模拟特定行为有关。在Spock中,这是存根可以做的所有事情,因此这是最简单的事情。
  • 模拟程序与代替一个(可能是昂贵的)真实对象有关,为所有方法调用提供无操​​作的答案。在这方面,模拟比存根更简单。但是在Spock中,模拟也可以对方法结果进行存根处理,即既是模拟又是存根。此外,在Spock中,我们可以计算在测试过程中调用具有某些参数的特定模拟方法的频率。
  • spy 总是包裹一个真实的对象,并且默认情况下将所有方法调用路由到原始对象,同时还会传递原始结果。方法调用计数也适用于 spy 。在Spock中, spy 还可以修改原始对象的行为,操纵方法调用参数和/或结果或完全阻止调用原始方法。

  • 现在,这里是一个可执行的示例测试,演示了什么是可能的,什么不是。比mikhail的摘要更具启发性。非常感谢他激励我改善自己的答案! :-)

    package de.scrum_master.stackoverflow
    
    import org.spockframework.mock.TooFewInvocationsError
    import org.spockframework.runtime.InvalidSpecException
    import spock.lang.FailsWith
    import spock.lang.Specification
    
    class MockStubSpyTest extends Specification {
    
      static class Publisher {
        List<Subscriber> subscribers = new ArrayList<>()
    
        void addSubscriber(Subscriber subscriber) {
          subscribers.add(subscriber)
        }
    
        void send(String message) {
          for (Subscriber subscriber : subscribers)
            subscriber.receive(message);
        }
      }
    
      static interface Subscriber {
        String receive(String message)
      }
    
      static class MySubscriber implements Subscriber {
        @Override
        String receive(String message) {
          if (message ==~ /[A-Za-z ]+/)
            return "ok"
          return "uh-oh"
        }
      }
    
      Subscriber realSubscriber1 = new MySubscriber()
      Subscriber realSubscriber2 = new MySubscriber()
      Publisher publisher = new Publisher(subscribers: [realSubscriber1, realSubscriber2])
    
      def "Real objects can be tested normally"() {
        expect:
        realSubscriber1.receive("Hello subscribers") == "ok"
        realSubscriber1.receive("Anyone there?") == "uh-oh"
      }
    
      @FailsWith(TooFewInvocationsError)
      def "Real objects cannot have interactions"() {
        when:
        publisher.send("Hello subscribers")
        publisher.send("Anyone there?")
    
        then:
        2 * realSubscriber1.receive(_)
      }
    
      def "Stubs can simulate behaviour"() {
        given:
        def stubSubscriber = Stub(Subscriber) {
          receive(_) >>> ["hey", "ho"]
        }
    
        expect:
        stubSubscriber.receive("Hello subscribers") == "hey"
        stubSubscriber.receive("Anyone there?") == "ho"
        stubSubscriber.receive("What else?") == "ho"
      }
    
      @FailsWith(InvalidSpecException)
      def "Stubs cannot have interactions"() {
        given: "stubbed subscriber registered with publisher"
        def stubSubscriber = Stub(Subscriber) {
          receive(_) >> "hey"
        }
        publisher.addSubscriber(stubSubscriber)
    
        when:
        publisher.send("Hello subscribers")
        publisher.send("Anyone there?")
    
        then:
        2 * stubSubscriber.receive(_)
      }
    
      def "Mocks can simulate behaviour and have interactions"() {
        given:
        def mockSubscriber = Mock(Subscriber) {
          3 * receive(_) >>> ["hey", "ho"]
        }
        publisher.addSubscriber(mockSubscriber)
    
        when:
        publisher.send("Hello subscribers")
        publisher.send("Anyone there?")
    
        then: "check interactions"
        1 * mockSubscriber.receive("Hello subscribers")
        1 * mockSubscriber.receive("Anyone there?")
    
        and: "check behaviour exactly 3 times"
        mockSubscriber.receive("foo") == "hey"
        mockSubscriber.receive("bar") == "ho"
        mockSubscriber.receive("zot") == "ho"
      }
    
      def "Spies can have interactions"() {
        given:
        def spySubscriber = Spy(MySubscriber)
        publisher.addSubscriber(spySubscriber)
    
        when:
        publisher.send("Hello subscribers")
        publisher.send("Anyone there?")
    
        then: "check interactions"
        1 * spySubscriber.receive("Hello subscribers")
        1 * spySubscriber.receive("Anyone there?")
    
        and: "check behaviour for real object (a spy is not a mock!)"
        spySubscriber.receive("Hello subscribers") == "ok"
        spySubscriber.receive("Anyone there?") == "uh-oh"
      }
    
      def "Spies can modify behaviour and have interactions"() {
        given:
        def spyPublisher = Spy(Publisher) {
          send(_) >> { String message -> callRealMethodWithArgs("#" + message) }
        }
        def mockSubscriber = Mock(MySubscriber)
        spyPublisher.addSubscriber(mockSubscriber)
    
        when:
        spyPublisher.send("Hello subscribers")
        spyPublisher.send("Anyone there?")
    
        then: "check interactions"
        1 * mockSubscriber.receive("#Hello subscribers")
        1 * mockSubscriber.receive("#Anyone there?")
      }
    }
    

    关于testing - Spock测试框架中的Mock/Stub/Spy之间的区别,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24413184/

    10-11 00:47