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

查看:25
本文介绍了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.

推荐答案

注意:我将在接下来的段落中过度简化,甚至可能略有造假.有关更多详细信息,请参阅 Martin Fowler 的网站.

模拟是一个虚拟类替换真实的类,为每个方法调用返回类似 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天全站免登陆