spockframework / spock

The Enterprise-ready testing and specification framework.
https://spockframework.org
Apache License 2.0
3.52k stars 1 forks source link

thrown check doesn't handle checked exceptions with mocks #420

Open robfletcher opened 8 years ago

robfletcher commented 8 years ago

Originally reported on Google Code with ID 298

In a Mock throw a checked exception

   1 * repository.findOne(42l) >> {throw new NotFoundException("oh no!")}

In the "then" clause use the thrown check to verify the exception

   thrown(NotFoundException)

The result is a test failure because groovy wraps the exception with UndeclaredThrowableException.
I think thrown should check for this exception type and check if the type matches the
"undeclaredThrowable" property of UndeclaredThrowableException.

Exception:

Expected exception com.fl.rest.NotFoundException, but got java.lang.reflect.UndeclaredThrowableException
    at spock.lang.Specification.thrown(Specification.java:232)
    at ...###Test.read a bean(###ControllerTest.groovy:47)
Caused by: java.lang.reflect.UndeclaredThrowableException
    at ...###Controller.readBean_aroundBody0(###Controller.java:32)
    at ...###Controller.readBean_aroundBody1$advice(###Controller.java:31)
    at ...###Controller.readBean(###Controller.java:1)
    at ...###ControllerTest.read a bean(###ControllerTest.groovy:43)
Caused by: ....NotFoundException: oh no!
    at ...###ControllerTest.read a bean_closure3(###ControllerTest.groovy:46)
    at groovy.lang.Closure.call(Closure.java:412)
    at org.spockframework.util.GroovyRuntimeUtil.invokeClosure(GroovyRuntimeUtil.java:130)
    at org.spockframework.mock.CodeResultGenerator.generateSingle(CodeResultGenerator.java:38)
    at org.spockframework.mock.SingleResultGenerator.generate(SingleResultGenerator.java:28)
    at org.spockframework.mock.ResultGeneratorChain.generate(ResultGeneratorChain.java:46)
    at org.spockframework.mock.MockInteraction.accept(MockInteraction.java:60)
    at org.spockframework.mock.MockInteractionDecorator.accept(MockInteractionDecorator.java:41)
    at org.spockframework.mock.InteractionScope$1.accept(InteractionScope.java:38)
    at org.spockframework.mock.MockController.dispatch(MockController.java:42)
    at org.spockframework.mock.DefaultMockFactory$1.invoke(DefaultMockFactory.java:70)
    ... 4 more

What version of Spock and Groovy are you using?
0.6-groovy-1.8

Please provide any additional information below. You can also assign
labels.

It's a simple change to the thrown method of Specification that would make working
with checked exceptions & mocks a lot easier.

Reported by otatop@otatop.com on 2013-02-11 16:37:19

robfletcher commented 8 years ago
A one liner fix would be the following code added to the top of the thrown method. 

def realException = exception instanceof UndeclaredThrowableException ?  exception.undeclaredThrowable
: exception;

... 

Reported by otatop@otatop.com on 2013-02-11 17:15:17

robfletcher commented 8 years ago
The problem stems from the fact that you are throwing a checked exception from a method
that doesn't declare one. We should probably allow that for a GroovyMock() (because
Groovy doesn't have checked exceptions), but I'm unsure if we should allow the same
for a Mock().

Reported by pniederw on 2013-02-21 17:38:49

robfletcher commented 8 years ago
From what I've found on the Internets about UndeclaredThrowableException, this is actually
Java7 doing the wrapping, and it happens when a dynamic proxy (which is what Mocks
and Closures are) throws a checked exception that isn't declared on the method being
proxied.

Catching and unwrapping so that the Throws clause works correctly is on'y 1/2 of the
battle.

Reported by cdeszaq on 2015-01-27 16:27:31