With RestEasy in a JBoss container, I have an ExceptionMapper
annotated with @Provider
, which has access to the HttpServletRequest
and HttpServletResponse
via @Context
annotations, like this:
@Provider
public class MyExceptionMapper implements ExceptionMapper<Throwable> {
@Context
private HttpServletRequest httpServletRequest;
@Context
private HttpServletResponse httpServletResponse;
@Override
public Response toResponse(final Throwable exception) {
...
return response;
}
}
I'm new to RestEasy, coming from a Spring background, so naively assumed I'd be able to inject those two fields and mock them in a unit test, but that seems harder than I'd expect!
In case the mocking framework is relevant, I'm using JMockit, and am also new to that. So far I've been able to apply my knowledge of Mockito to it with a good degree of success.
I'm not finding much by searching around the subject, other than lots of suggestions to run an embedded container in my unit tests. I'm not completely opposed to that, but it feels like overkill when I'm just trying to write a simple unit test.
I've tried several things in the test, most recently this:
public class MyExceptionMapperTest {
@Injectable
private HttpServletRequest httpServletRequest;
@Injectable
private HttpServletResponse httpServletResponse;
@Tested
private MyExceptionMapper exceptionMapper;
@Test
public void test() {
exceptionMapper.toResponse(new Throwable());
}
}
But this results in a NullPointerException
in MyExceptionMapper
the first time I refer to one of the @Context
fields, which tells me that they are not being injected.
I've also tried:
@Mocked
instead of @Injectable
;MyExceptionMapper
both with and without the @Tested
annotation;Expectations
in my test; andIn all cases the @Context
-annotated fields are null
.
Hopefully I'm just missing something very obvious?
In the absence of a better answer, I've ended up using reflection in my tests to inject the value for these two properties. Not ideal, but it works:
public class MyExceptionMapperTest {
@Mocked
private HttpServletRequest mockHttpServletRequest;
@Mocked
private HttpServletResponse mockHttpServletResponse;
private MyExceptionMapper exceptionMapper = new MyExceptionMapper();
@Before
public void setup() {
Field httpServletRequest = exceptionMapper.getClass().getDeclaredField("httpServletRequest");
httpServletRequest.setAccessible(true);
httpServletRequest.set(exceptionMapper, mockHttpServletRequest);
Field httpServletResponse = exceptionMapper.getClass().getDeclaredField("httpServletResponse");
httpServletResponse.setAccessible(true);
httpServletResponse.set(exceptionMapper, mockHttpServletResponse);
}
}
Then in my tests I can set expectations and verifications on the two @Mocked
values as normal.
I'm no expert on reflection, so this probably isn't the best/most efficient approach, but it's good enough for my needs.