Spock测试框架中的Mock/Stub/Spy之间的区别 [英] Difference between Mock / Stub / Spy in Spock test framework

查看:864
本文介绍了Spock测试框架中的Mock/Stub/Spy之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不了解Spock测试中的Mock,Stub和Spy之间的区别,而我一直在网上查看的教程也没有详细解释它们.

I don't understand the difference between Mock, Stub, and Spy in Spock testing and the tutorials I have been looking at online don't explain them in detail.

推荐答案

注意:在接下来的几段中,我将过于简化,甚至稍加捏造.有关更多详细信息,请参见马丁·福勒的网站.

Attention: I am going to oversimplify and maybe even slightly falsify in the upcoming paragraphs. For more detailed info see Martin Fowler's website.

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

A mock is a dummy class replacing a real one, returning something like null or 0 for each method call. You use a mock if you need a dummy instance of a complex class which would otherwise use external resources like network connections, files or databases or maybe use dozens of other objects. The advantage of mocks is that you can isolate the class under test from the rest of the system.

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

A stub is also a dummy class providing some more specific, prepared or pre-recorded, replayed results to certain requests under test. You could say a stub is a fancy mock. In Spock you will often read about stub methods.

间谍是真实对象和存根之间的混合体,即它基本上是具有某些(不是全部)方法被存根方法所掩盖的真实对象.非存根方法只是被路由到原始对象.这样,您可以使便宜"或琐碎方法具有原始行为,而对昂贵"或复杂方法具有虚假行为.

A spy is kind of a hybrid between real object and stub, i.e. it is basically the real object with some (not all) methods shadowed by stub methods. Non-stubbed methods are just routed through to the original object. This way you can have original behaviour for "cheap" or trivial methods and fake behaviour for "expensive" or complex methods.

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

Update 2017-02-06: Actually user mikhail's answer is more specific to Spock than my original one above. So within the scope of Spock, what he describes is correct, but that does not falsify my general answer:

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

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

Now here is an executable example test, demonstrating what is possible and what is not. It is a bit more instructive than mikhail's snippets. Many thanks to him for inspiring me to improve my own answer! :-)

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?")
  }
}

这篇关于Spock测试框架中的Mock/Stub/Spy之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆