spock测试桩mock的使用

标签:

本文出自jvm123.com-java技术分享站:http://jvm123.com/2019/09/spock-ce-shi-zhuang-2.html

上一篇详细介绍了spock框架的基本使用

mock测试桩 使用场景

mock测试桩的使用场景是:调用一个方法后,测试需要达到的效果。例如以下代码:

    def subscriber = Mock(Subscriber)   // 1. 创建一个mock对象

    def "should send messages subscriber"() {
        when:
        publisher.send("hello")         // 2. publisher 发送一个“hello”
        then:
        1 * subscriber.receive("hello") // 3. subscriber 接收到一个“hello”
    }

上面测试代码测试的是publisher的send方法,如果send一个“hello”,我们期望subscriber收到一个“hello”。如果收不到、或收到其他字符串、或收到多次都不能通过测试。

而这个subscriber是我们使用mock工厂创建的一个测试桩假对象,我们根本不需要知道recieve方法的实现,就可以完成这个测试。

以上就是mock测试桩的一个主要使用场景。下面介绍一个使用实例。

mock测试桩 使用实例

class MockSpec extends Specification {
    Publisher publisher = new Publisher()
    def subscriber = Mock(Subscriber)
    def subscriber2 = Mock(Subscriber)

    def setup() {
        publisher.subscribers << subscriber // << 相当于 list.add()
        publisher.subscribers << subscriber2
    }

    def "should send messages to all subscribers"() {
        when:
        publisher.send("hello")

        then:
        1 * subscriber.receive("hello")
        1 * subscriber2.receive("hello")
    }

}

class Publisher {
    List<Subscriber> subscribers = []
    int messageCount = 0
    void send(String message){
        // *. 分别调用list每个元素的receive方法
        subscribers*.receive(message)
        messageCount++
    }
}

interface Subscriber {
    void receive(String message)
}

以上代码测试的目的是:当调用 publisher.send("hello") 后,我们期望两个subscriber都会接收到一次“hello”。如果出现其他结果,测试就算失败。

如果我们将上述测试代码修改:当发送“hello”后,我们期望接收到“hello1”,如下

    def "should send messages to all subscribers"() {
        when:
        publisher.send("hello")

        then:
        1 * subscriber.receive("hello1")
        1 * subscriber2.receive("hello1")
    }

这个测试很明显不会通过,将会出现以下结果:

spock测试桩mock的使用插图

失败的原因是只调用了0次recieve(“hello1”)方法,即subscriber没有接收到“hello1”。

mock then后面结果的断言

对于mock then后面结果的断言,有以下的格式:

1 调用频率约束

1 * subscriber.receive("hello")      // exactly one call
0 * subscriber.receive("hello")      // zero calls
(1..3) * subscriber.receive("hello") // between one and three calls (inclusive)
(1.._) * subscriber.receive("hello") // at least one call
(_..3) * subscriber.receive("hello") // at most three calls
_ * subscriber.receive("hello")      // any number of calls, including zero
                                     // (rarely needed; see 'Strict Mocking')

2 调用对象约束

1 * subscriber.receive("hello") // a call to 'subscriber'
1 * _.receive("hello")          // a call to any mock object

3 调用方法约束

1 * subscriber.receive("hello") // a method named 'receive'
1 * subscriber./r.*e/("hello")  // a method whose name matches the given regular expression
                                // (here: method name starts with 'r' and ends in 'e')

其中getter setter方法的调用可以用如下格式:

When expecting a call to a getter method, Groovy property syntax can be used instead of method syntax:

1 * subscriber.status // same as: 1 * subscriber.getStatus()

When expecting a call to a setter method, only method syntax can be used:

1 * subscriber.setStatus("ok") // NOT: 1 * subscriber.status = "ok"

4 调用参数约束

1 * subscriber.receive("hello")        // an argument that is equal to the String "hello"
1 * subscriber.receive(!"hello")       // an argument that is unequal to the String "hello"
1 * subscriber.receive()               // the empty argument list (would never match in our example)
1 * subscriber.receive(_)              // any single argument (including null)
1 * subscriber.receive(*_)             // any argument list (including the empty argument list)
1 * subscriber.receive(!null)          // any non-null argument
1 * subscriber.receive(_ as String)    // any non-null argument that is-a String
1 * subscriber.receive(endsWith("lo")) // any non-null argument that is-a String
1 * subscriber.receive({ it.size() > 3 && it.contains('a') })
// an argument that satisfies the given predicate, meaning that
// code argument constraints need to return true of false
// depending on whether they match or not
// (here: message length is greater than 3 and contains the character a)

其中多参数和可变参数的方法可用以下方式:

Argument constraints work as expected for methods with multiple arguments:

1 * process.invoke("ls", "-a", _, !null, { ["abcdefghiklmnopqrstuwx1"].contains(it) })

When dealing with vararg methods, vararg syntax can also be used in the corresponding interactions:

interface VarArgSubscriber {
    void receive(String... messages)
}

...

subscriber.receive("hello", "goodbye")

5 任意方法调用

1 * subscriber._(*_)     // any method on subscriber, with any argument list
1 * subscriber._         // shortcut for and preferred over the above

1 * _._                  // any method call on any mock object
1 * _                    // shortcut for and preferred over the above

Although (..) * .(*_) >> _ is a valid interaction declaration, it is neither good style nor particularly useful.

下一篇文章介绍在spring环境中使用spock

发表评论