I mentioned that sometimes using mocks created via Map coercion in Groovy didn’t always work as expected. Here’s a concrete example.
Here’s an exception handling code block that I’m trying to test, removing everything except the relevant bits:
public void performActivity() throws ActivityException { try { // create a FileManager using factory passed into object constructor final FileManager fileManager = fileManagerFactory.make(params); // do stuff with it } catch (FileManagerException e) { throw new ActivityException(null, e); } }
I’m writing a test to verify that an ActivityException is returned when a FileManagerException is caught. My first take on the Groovy test method was:
public void testPerformActivity_exceptionInFileManagerFactory() { def factory = [make: {throw new FileManagerException('testing')}] as FileManagerFactory // do other test setup def test = new Worker(factory) shouldFail(ActivityException) { test.performActivity() } }
Seems pretty straightforward, right? A call to make() in the factory throws a FileManagerException . The method being tested catches that exception, wraps and rethrows it as an ActivityException . The test asserts this takes place. Should work.
Except when the test is run, it fails with the error message:
should have failed with an exception of type vue.workflow.ActivityException, instead got Exception vue.core.ftp.FileManagerException: testing
For some reason, the closure that’s executed as the mock’s make() operation is wrapping the exception that’s thrown in the closure within an Exception , so the catch block being tested doesn’t match. I haven’t done any digging to see if this is a bug or whether there’s a Groovy workaround. It was faster in this case to just use EasyMock instead:
public void testWork_exceptionInFileManagerFactory() throws Exception { // Using Mocks to get correct exception behavior def factory = EasyMock.createMock(FileManagerFactory) as FileManagerFactory EasyMock.expect(factory.make(EasyMock.anyObject())).andThrow(new FileManagerException('testing')).anyTimes() EasyMock.replay(factory) // do other test setup def test = new Worker(factory) shouldFail(ActivityException) { test.performActivity(workOrder) } EasyMock.verify(factory) }
This test passes. So just watch out when throwing exceptions from within Map coercion mocks; the results may be unexpected!