【问题标题】:Stubbed object method value not returned in Spock testSpock 测试中未返回存根对象方法值
【发布时间】:2021-01-15 12:40:56
【问题描述】:

我是 Spock 框架的新手,正在编写一个测试用例,我试图在其中模拟一个名为 QueryDatabase 的类

public class QueryDatabase {

    public BigInteger countRecords(Instant start, Instant end) {

        Flux<CountRecord> countValue = query("select * from users");
        Optional<Object> value = Optional.ofNullable(countValue.blockFirst()).map(CountRecord::getValue);
        BigInteger count = value.filter(BigInteger.class::isInstance)
                    .map(BigInteger.class::cast).orElse(BigInteger.valueOf(0));

        return count
    }
    
    public Flux<CountRecord> query(String query) {
    
    }
}

但是下面的测试用例是检查 countRecords(Instant, Instant) 返回的值总是给出 0,这意味着在 when 部分中返回的值

recordCount.query(_) >> Flux.just(CountRecord.builder().value(new BigInteger(133)).build())

没有被使用,似乎recordCount.query(_) &gt;&gt; Flux.empty()也没有任何影响,它总是返回默认的BigInteger值0

def "record count"() {

        given:
        def now = Instant.now()
        def last10Minutes = now.minus(10, ChronoUnit.MINUTES);
        def recordCount = Stub(QueryDatabase)

        when: "query returning empty flux"
        recordCount.query(_) >> Flux.empty()

        then:
        recordCount.countRecords(last10Minutes, now) == 0

        when: "query returning the count record"
        recordCount.query(_) >> Flux.just(CountRecord.builder().value(new BigInteger(133)).build())

        then:
        recordCount.countRecords(last10Minutes, now) == 133

 }

我在这里做错了吗?

【问题讨论】:

    标签: java unit-testing mocking spock stubbing


    【解决方案1】:

    您的代码有几个问题。

    1. 您尝试在 when 块中设置一些存根
    2. 您在 then 块中执行您的操作
    3. 您尝试重新定义存根

    请参阅 Combining Mocking and Stubbing 了解其工作原理。

    def "record count"() {    
            given:
            def now = Instant.now()
            def last10Minutes = now.minus(10, ChronoUnit.MINUTES);
            def recordCount = Spy(QueryDatabase)
    
            when: "query returning empty flux"
            def result = recordCount.countRecords(last10Minutes, now)
    
            then:            
            1 * recordCount.query(_) >> Flux.empty()
            result == 0
    
            when: "query returning the count record"
            def 2ndResult = recordCount.countRecords(last10Minutes, now) == 133
    
            then:            
            1 * recordCount.query(_) >> Flux.just(CountRecord.builder().value(new BigInteger(133)).build())
            2ndResult == 133    
     }
    

    或者,您可以将其拆分为数据驱动功能

    def "record count"(BigInteger result, Flux input) {    
            given:
            def now = Instant.now()
            def last10Minutes = now.minus(10, ChronoUnit.MINUTES);
            def recordCount = Spy(QueryDatabase)
            recordCount.query(_) >> input
    
            expect:          
            recordCount.countRecords(last10Minutes, now) == result
    
            where: 
            result | input
            0      | Flux.empty()
            133    | Flux.just(CountRecord.builder().value(new BigInteger(133)).build())
     }
    

    通常情况下,您会以相反的方式对参数进行排序,但由于通量非常冗长,我觉得这更易读。

    -- 编辑:

    我错过了您正在尝试对您正在测试的同一个对象进行存根,这只能通过 partial mocking 完成,并且通常表明应该重构代码。因此,将Mock/Stub 替换为Spy 以进行部分模拟。

    【讨论】:

    • 感谢 Leonard 调查此事。我在您建议的选项 1 中收到此错误......调用太少:1 * recordCount.query(_) >> Flux.empty()(0 调用)不匹配的调用(按相似度排序):1 * recordCount.countRecords(2021-01-17T10:21:29.140140Z, 2021-01-17T10:31:29.140140Z)
    • @NishitJain 我已经更新了答案和代码。
    【解决方案2】:

    当您覆盖query 方法以返回预期的Flux 时,这种方式会怎样。在这种情况下,我建议进行 2 次测试:

    def "record count when empty Flux"() {
            given:
            def now = Instant.now()
            def last10Minutes = now.minus(10, ChronoUnit.MINUTES);
            def recordCount = new QueryDatabase() {
              @Overriden
              public Flux<CountRecord> query(String query) {
                Flux.empty()
              }
            }
    
            expect:
            recordCount.countRecords(last10Minutes, now) == 0
     }
    
    def "record count when non empty Flux"() {
            given:
            def now = Instant.now()
            def last10Minutes = now.minus(10, ChronoUnit.MINUTES);
            def recordCount = new QueryDatabase() {
              @Overriden
              public Flux<CountRecord> query(String query) {
                Flux.just(CountRecord.builder().value(new BigInteger(133)).build())
              }
            }
    
            expect:
            recordCount.countRecords(last10Minutes, now) == 133
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-01
      • 2015-08-23
      • 2017-07-19
      相关资源
      最近更新 更多