exception - Spock 在 Where 块中测试异常处理
问题描述
我正在测试一种具有一些依赖关系的服务方法;我想断言,如果这些依赖项中的任何一个引发异常,服务方法应该返回一个默认值。
我想写的服务和测试看起来像这样。
static class Service {
def dependency1
def dependency2
def dependency3
def method() {
try {
def foo = dependency1.get()
def bar = dependency2.get()
def baz = dependency3.get()
return " $foo $bar $baz "
} catch (Exception e) {
println e
return ' default value '
}
}
}
def 'test Service error handling'() {
given:
def dependency1 = Mock(Supplier)
def dependency2 = Mock(Supplier)
def dependency3 = Mock(Supplier)
def serviceUnderTest = new Service(dependency1: dependency1, dependency2: dependency2, dependency3: dependency3)
when:
def result = serviceUnderTest.method()
then:
result == ' default value '
dependency1.get() >> closure1
dependency2.get() >> closure2
dependency3.get() >> closure3
where:
closure1 | closure2 | closure3
{-> throw new Exception('closure1') } | {-> null } | {-> null };
{-> null} | {-> throw new Exception('closure2') } | {-> null };
{-> null} | {-> null} | {-> throw new Exception('closure3') }
}
这个测试不起作用,因为它导致模拟返回文字闭包而不是那些闭包的结果。当然这是由于添加了where
块造成的,因为任何mock都可以直接返回单个闭包的结果,即dependency1.get() >> { throw new Exception() }
我是否被迫将其编写为三个单独的测试,还是有另一种方法可以将它们结合起来?
解决方案
如果你写
dependency1.get() >> closure1
您的模拟将返回闭包本身而不是对其进行评估。评估只发生在 GroovyString 内部" $foo $bar $baz "
,将评估期间发生的错误消息扩展到其中,但不会升级该异常。
你想用
dependency1.get() >> { closure1() }
为了修复你的测试。评估您的()
闭包,但同时周围的闭包{}
确保评估仅在调用存根方法时发生,而不是在定义它时发生。
一些改进的想法:
- 如何展开您的测试,将其拆分为多个具有参数化名称的方法?这还具有很好的副作用,可以帮助 IDE 和 Groovy 编译器在
where:
不使用分号和{ -> ...
语法的情况下解析您的块。 given:
在块中而不是then:
在它们不属于的块中存根模拟方法怎么样?- 如何在模拟定义中存根方法以使测试更紧凑?
when: ... then:
在这种简单的情况下,如何expect:
按照 Spock 手册的建议替换?
package de.scrum_master.stackoverflow.q57172322
import spock.lang.Specification
import spock.lang.Unroll
class ServiceDependenciesThrowingErrorsTest extends Specification {
@Unroll
def 'handle error in service #serviceName'() {
given:
def serviceUnderTest = new Service(
dependency1: Mock(Supplier) { get() >> { closure1() } },
dependency2: Mock(Supplier) { get() >> { closure2() } },
dependency3: Mock(Supplier) { get() >> { closure3() } }
)
expect:
serviceUnderTest.method() == 'default value'
where:
serviceName | closure1 | closure2 | closure3
"A" | { throw new Exception('closure1') } | { null } | { null }
"B" | { null } | { throw new Exception('closure2') } | { null }
"C" | { null } | { null } | { throw new Exception('closure3') }
}
static class Service {
def dependency1
def dependency2
def dependency3
def method() {
try {
def foo = dependency1.get()
def bar = dependency2.get()
def baz = dependency3.get()
return "$foo $bar $baz"
} catch (Exception e) {
println e
return 'default value'
}
}
}
static class Supplier {
def get() {
"OK"
}
}
}
这是展开时测试执行在我的 IDE (IntelliJ IDEA) 中的样子:
推荐阅读
- socket.io - 有没有办法通过socket.io向客户端发送图像?
- excel - excel中不同数据的累积百分比
- php - FollowSymLinks 不适用于 laravel 8
- python - 在透视后删除更高级别的索引名称
- puppeteer - Puppeteer 测试运行不一致
- swift - Swift等效于while循环与python中的else
- file - 如何在javascript中使用puppeteer获取默认文件目录
- python - Selenium 按钮不可点击的 Python
- javascript - 在 chrome 扩展中使用 content.js 为按钮添加图像
- nginx - connect() 失败(每个套接字地址(协议/网络地址/端口)通常只允许使用一次)同时连接到上游