Spock 在 Where 块中测试异常处理
Spock testing exception handling in a Where block
我正在测试一个有一些依赖关系的服务方法;我想断言,如果这些依赖项中的任何一个抛出异常,服务方法应该 return 默认值。
我想编写的服务和测试看起来像这样。
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') }
}
此测试不起作用,因为它导致模拟 return 文字闭包而不是这些闭包的结果。当然这是由于添加了 where
块造成的,因为任何 mock 都可以直接 return 单个闭包的结果,即 dependency1.get() >> { throw new Exception() }
我是否被迫将其写为三个单独的测试,或者是否有其他方法可以将它们组合起来?
如果你写
dependency1.get() >> closure1
您的模拟将 return 闭包本身而不对其进行评估。评估仅发生在 GroovyString " $foo $bar $baz "
内部,将评估期间发生的错误消息扩展到其中,但不会升级该异常。
您想使用
dependency1.get() >> { closure1() }
为了修复你的测试。 ()
评估您的闭包,但同时周围的闭包 {}
确保评估仅在调用存根方法时发生,而不是在定义时发生。
几点改进意见:
- 如何展开您的测试,将其拆分为多个具有参数化名称的方法?这也有很好的副作用,可以帮助 IDE 和 Groovy 编译器解析没有分号和
{ -> ...
语法的 where:
块。
- 如何在
given:
块中而不是在它们不属于的 then:
块中存根模拟方法?
- 如何在 mock 定义中添加方法以使测试更紧凑?
- 如何按照 Spock 手册的建议将这个简单案例中的
when: ... then:
替换为 expect:
?
package de.scrum_master.Whosebug.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) 展开时测试执行的样子:
我正在测试一个有一些依赖关系的服务方法;我想断言,如果这些依赖项中的任何一个抛出异常,服务方法应该 return 默认值。
我想编写的服务和测试看起来像这样。
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') }
}
此测试不起作用,因为它导致模拟 return 文字闭包而不是这些闭包的结果。当然这是由于添加了 where
块造成的,因为任何 mock 都可以直接 return 单个闭包的结果,即 dependency1.get() >> { throw new Exception() }
我是否被迫将其写为三个单独的测试,或者是否有其他方法可以将它们组合起来?
如果你写
dependency1.get() >> closure1
您的模拟将 return 闭包本身而不对其进行评估。评估仅发生在 GroovyString " $foo $bar $baz "
内部,将评估期间发生的错误消息扩展到其中,但不会升级该异常。
您想使用
dependency1.get() >> { closure1() }
为了修复你的测试。 ()
评估您的闭包,但同时周围的闭包 {}
确保评估仅在调用存根方法时发生,而不是在定义时发生。
几点改进意见:
- 如何展开您的测试,将其拆分为多个具有参数化名称的方法?这也有很好的副作用,可以帮助 IDE 和 Groovy 编译器解析没有分号和
{ -> ...
语法的where:
块。 - 如何在
given:
块中而不是在它们不属于的then:
块中存根模拟方法? - 如何在 mock 定义中添加方法以使测试更紧凑?
- 如何按照 Spock 手册的建议将这个简单案例中的
when: ... then:
替换为expect:
?
package de.scrum_master.Whosebug.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) 展开时测试执行的样子: