Ren*_*ibe 47 testing groovy exception spock
如何用Spock以一种很好的方式测试异常(例如数据表)?
示例:具有validateUser
可以使用不同消息抛出异常的方法,或者如果用户有效则无异常.
规范类本身:
class User { String userName }
class SomeSpec extends spock.lang.Specification {
...tests go here...
private validateUser(User user) {
if (!user) throw new Exception ('no user')
if (!user.userName) throw new Exception ('no userName')
}
}
Run Code Online (Sandbox Code Playgroud)
变式1
这一个是工作,但真正的意图是所有的混乱时,/然后标签和的多次呼吁validateUser(user)
.
def 'validate user - the long way - working but not nice'() {
when:
def user = new User(userName: 'tester')
validateUser(user)
then:
noExceptionThrown()
when:
user = new User(userName: null)
validateUser(user)
then:
def ex = thrown(Exception)
ex.message == 'no userName'
when:
user = null
validateUser(user)
then:
ex = thrown(Exception)
ex.message == 'no user'
}
Run Code Online (Sandbox Code Playgroud)
变体2
由于Spock在编译时引发了这个错误,因此无法正常工作:
仅在'then'块中允许例外条件
def 'validate user - data table 1 - not working'() {
when:
validateUser(user)
then:
check()
where:
user || check
new User(userName: 'tester') || { noExceptionThrown() }
new User(userName: null) || { Exception ex = thrown(); ex.message == 'no userName' }
null || { Exception ex = thrown(); ex.message == 'no user' }
}
Run Code Online (Sandbox Code Playgroud)
变式3
由于Spock在编译时引发了这个错误,因此无法正常工作:
异常条件仅允许作为顶级语句
def 'validate user - data table 2 - not working'() {
when:
validateUser(user)
then:
if (expectedException) {
def ex = thrown(expectedException)
ex.message == expectedMessage
} else {
noExceptionThrown()
}
where:
user || expectedException | expectedMessage
new User(userName: 'tester') || null | null
new User(userName: null) || Exception | 'no userName'
null || Exception | 'no user'
}
Run Code Online (Sandbox Code Playgroud)
Pet*_*ser 46
推荐的解决方案是有两种方法:一种测试好的情况,另一种测试坏的情况.然后这两种方法都可以使用数据表.
例:
class SomeSpec extends Specification {
class User { String userName }
def 'validate valid user'() {
when:
validateUser(user)
then:
noExceptionThrown()
where:
user << [
new User(userName: 'tester'),
new User(userName: 'joe')]
}
def 'validate invalid user'() {
when:
validateUser(user)
then:
def error = thrown(expectedException)
error.message == expectedMessage
where:
user || expectedException | expectedMessage
new User(userName: null) || Exception | 'no userName'
new User(userName: '') || Exception | 'no userName'
null || Exception | 'no user'
}
private validateUser(User user) {
if (!user) throw new Exception('no user')
if (!user.userName) throw new Exception('no userName')
}
}
Run Code Online (Sandbox Code Playgroud)
这是我想出的解决方案。它基本上是变体 3,但它使用一个try/catch
块来避免使用 Spock 的异常条件(因为它们必须是顶级的)。
def "validate user - data table 3 - working"() {
expect:
try {
validateUser(user)
assert !expectException
}
catch (UserException ex)
{
assert expectException
assert ex.message == expectedMessage
}
where:
user || expectException | expectedMessage
new User(userName: 'tester') || false | null
new User(userName: null) || true | 'no userName'
null || true | 'no user'
}
Run Code Online (Sandbox Code Playgroud)
一些注意事项:
assert
在 try/catch 块内使用显式条件(语句)。when-then
块。这是我的做法,我修改了when:
子句以始终抛出Success
异常,这样您就不需要单独的测试或逻辑来判断是否调用thrown
or notThrown
,只需始终thrown
使用数据表进行调用即可告知是否期望Success
。
您可以重命名Success
为None
或NoException
或任何您喜欢的名称。
class User { String userName }
class SomeSpec extends spock.lang.Specification {
class Success extends Exception {}
def 'validate user - data table 2 - working'() {
when:
validateUser(user)
throw new Success ()
then:
def ex = thrown(expectedException)
ex.message == expectedMessage
where:
user || expectedException | expectedMessage
new User(userName: 'tester') || Success | null
new User(userName: null) || Exception | 'no userName'
null || Exception | 'no user'
}
private validateUser(User user) {
if (!user) throw new Exception ('no user')
if (!user.userName) throw new Exception ('no userName')
}
}
Run Code Online (Sandbox Code Playgroud)
我要更改的另一件事是也为失败异常使用子类,以避免Success
在您真正期待失败时被意外捕获。它不会影响您的示例,因为您对消息进行了额外检查,但其他测试可能只测试异常类型。
class Failure extends Exception {}
Run Code Online (Sandbox Code Playgroud)
并使用那个或其他一些“真正的”异常而不是香草 Exception
您可以使用返回消息或异常类的方法或两者的映射来包装方法调用.
def 'validate user - data table 2 - not working'() {
expect:
expectedMessage == getExceptionMessage(&validateUser,user)
where:
user || expectedMessage
new User(userName: 'tester') || null
new User(userName: null) || 'no userName'
null || 'no user'
}
String getExceptionMessage(Closure c, Object... args){
try{
return c.call(args)
//or return null here if you want to check only for exceptions
}catch(Exception e){
return e.message
}
}
Run Code Online (Sandbox Code Playgroud)
我有一个不会扭曲您的测试工作流程的解决方案,您可以通过放置在表中的动态对象的内容来分析异常
@Unroll
def "test example [a=#a, b=#b]"() {
given:
def response
def caughtEx
when:
try {
result = someAmazingFunctionWhichThrowsSometimes(a,b)
} catch (Exception ex) {
caughtEx = ex
}
then:
result == expected
if (exception.expected) {
assert caughtEx != null && exception.type.isInstance(caughtEx)
} else {
assert caughtEx == null
}
where:
a | b || exception | expected
8 | 4 || [expected: false] | 2
6 | 3 || [expected: false] | 3
6 | 2 || [expected: false] | 3
4 | 0 || [expected: true, type: RuntimeException] | null
}
Run Code Online (Sandbox Code Playgroud)