Testing exception handling with Groovy

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!

Leave a Reply

Your email address will not be published. Required fields are marked *