问题描述
我是 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(_) >> 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
}
我在这里做错了什么吗?
解决方法
您的代码有几个问题。
- 您尝试在
when
块中设置一些存根 - 您在
then
块中执行您的操作 - 您尝试重新定义存根
请参阅 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
以进行部分模拟。
当您重写 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
}