测试代码抛异常场景
`class ExceptionSpec extends Specification {def validateService = new ValidateService()@Unrolldef "验证UserInfo"() {when: "调用校验方法"validateService.validateUser(user)then: "捕获异常并设置需要验证的异常值"def exception = thrown(expectedException)exception.errorCode == expectedErrCodeexception.errorMessage == expectedMessagewhere: "验证用户的合法性"user || expectedException | expectedErrCode | expectedMessagenull || APIException | "1001" | "userInfo is null"new UserInfo(0, "") || APIException | "1002" | "id is not legal"new UserInfo(1, "") || APIException | "1003" | "name is not legal"}
}`
Spock内置thrown()方法,可以捕获调用业务代码抛出的预期异常并验证,再结合where表格的功能,可以很方便的覆盖多种自定义业务异常。
在then
标签里用到了Spock的thrown()
方法,这个方法可以捕获我们要测试的业务代码里抛出的异常。thrown()
方法的入参expectedException
,是我们自己定义的异常变量,这个变量放在where
标签里就可以实现验证多种异常情况的功能。expectedException
类型调用validateUser
方法里定义的APIException
异常,可以验证它所有的属性,errorCode
、errorMessage
是否符合预期值。
注意事项
在Spock中,异常条件(如thrown
和notThrown
)只能在then
块的顶层使用,不能嵌套在其他控制结构(如if
语句)中。这意味着你不能在if
语句内部使用thrown
方法。
为了测试不同输入数据下的方法行为,包括抛出异常和不抛出异常的情况,你需要将这些测试拆分为单独的测试用例。每个测试用例只测试一种情况:要么是抛出异常,要么是不抛出异常。
测试案例代码
// 测试捕获异常 RuntimeException 异常def "checkUrlThrowException"() {given:SSRFUtilwhen:SSRFUtil.checkUrl(requestUrl)then:def exception = thrown(expectedException)where:requestUrl || expectedException"https://baidu.com" || RuntimeException}// 测试不抛异常def "checkUrlNotException"() {given:SSRFUtilwhen:SSRFUtil.checkUrl(requestUrl)then:def exception = notThrown(expectedException)where:requestUrl || expectedException"https://xxx.aaa.com/xxx/xxxx.xlsx" || null"https://xxx.aaa.net/xxx/xxxx.xlsx" || null"https://xxx.vip.aaa.com/xxx/xxxx.xlsx" || null}
其它
下面的案例来自于 小美智能助理 的答案.
在Spock中,where
块用于数据驱动测试,通过提供多个输入数据集来测试方法的行为。在测试方法不抛出异常的情况下,可以在then
块中使用notThrown
方法来验证没有异常被抛出。你可以结合where
块和notThrown
方法来测试方法在不同输入数据下的行为。
以下是一个示例,展示了如何在where
块中测试方法在不同输入数据下不抛出异常的情况。
示例代码
假设你有一个Calculator
类,它的divide
方法在除以零时会抛出一个IllegalArgumentException
,在其他情况下正常工作。
class Calculator {int divide(int a, int b) {if (b == 0) {throw new IllegalArgumentException("Division by zero")}return a / b}
}
你可以使用where
块来测试divide
方法在不同输入数据下的行为,不抛出异常的情况如下:
import spock.lang.Specification
import spock.lang.Unrollclass CalculatorSpec extends Specification {@Unrolldef "should return correct result when dividing #a by #b"() {given:def calculator = new Calculator()when:def result = calculator.divide(a, b)then:result == expectednotThrown(Exception)where:a | b || expected10 | 2 || 515 | 3 || 520 | 4 || 5}
}
在这个示例中:
-
@Unroll
注解用于展开每个数据驱动的测试用例,以便更好地查看每个测试用例的结果。 -
where
块定义了测试数据,包括输入参数a
和b
以及期望的结果expected
。 - 在
then
块中,我们验证result
是否等于期望值,并确保没有抛出任何异常。
结合测试抛异常和不抛异常
你还可以在同一个测试方法中结合测试方法抛出异常和不抛出异常的情况:
import spock.lang.Specification
import spock.lang.Unrollclass CalculatorSpec extends Specification {@Unrolldef "should handle division correctly for a=#a and b=#b"() {given:def calculator = new Calculator()when:def result = calculator.divide(a, b)then:if (b == 0) {def e = thrown(IllegalArgumentException)e.message == "Division by zero"} else {result == expectednotThrown(Exception)}where:a | b || expected10 | 2 || 510 | 0 || _15 | 3 || 520 | 4 || 5}
}
在这个示例中:
-
@Unroll
注解用于展开每个数据驱动的测试用例,以便更好地查看每个测试用例的结果。 -
where
块定义了测试数据,包括输入参数a
和b
以及期望的结果expected
。 -
在
then
块中,我们使用了条件语句来分别处理抛出异常和不抛出异常的情况。- 如果
b == 0
,我们期望抛出IllegalArgumentException
,并验证异常的消息内容。 - 否则,我们验证
result
是否等于期望值,并确保没有抛出任何异常。
- 如果
通过这种方式,你可以在数据驱动的测试中测试方法在不同输入数据下是否抛出异常或不抛出异常,并验证方法的行为是否符合预期。