0

This is the class which i am trying to test,

    public class AuthErrorResponseWriter  {

        @Autowired
        TransResponse svcResponse;


        @Override
        public void writeResponse(HttpServletResponse response) {


            //Set the Http status
            response.setStatus(HttpStatus.FORBIDDEN.value());
            svcResponse.setMessage(Constants.AUTHENTICATION_ERROR);
            svcResponse.setStatus(HttpStatus.FORBIDDEN.toString());

            ObjectMapper mapper = new ObjectMapper();

            //Write the response
            try {
                Writer writer = response.getWriter();
                writer.write(mapper.writeValueAsString(svcResponse));
                writer.flush();
                writer.close();
            } catch (IOException ioex) {
                logger.error("Problem producing authentication error http response",ioex);  
            }
    }

}

The unit test code i have written is below,

RunWith(SpringRunner.class)
@WebMvcTest({AuthErrorResponseWriter  .class})
@ComponentScan("com.demo.service")
public class AuthErrorResponseWriterTest {

    @Mock
    HttpServletResponse responseMock;


    @Before 
    public void setUp(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testResponse(){

        TransResponse mockResponse = new TransResponse();

        mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
        mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());

        AuthErrorResponseWriter   authErrorWriter = new AuthErrorResponseWriter  (); 
        PrintWriter writerMock = mock(PrintWriter.class);

        try {
            when(responseMock.getWriter()).thenReturn(writerMock);
        } catch (IOException ioex) {
            //assertTrue(false);
        }
        authErrorWriter.writeResponse(responseMock);
        verify(responseMock).setStatus(HttpStatus.FORBIDDEN.value());

    }

}

When i execute this Junit, am getting a null pointer exception for

svcResponse.setMessage(Constants.AUTHENTICATION_ERROR);

svcResponse is null, even though i have mocked it.

Please can someone point to me why its not picking up the mock object and looking for the actual.

Also if my writing the Junit is a proper way?

Umar
  • 1,002
  • 3
  • 12
  • 29
  • `WebMvcTest` is used to test Spring MVC component. Is `AuthErrorResponseWriter` a controller ? Then, I see you instantiate `AuthErrorResponseWriter` yourself, so doesn't do anything. Then, you do not mock `SvcResponse` at all, so it's null. When using `WebMvcTest`, no real context is loaded (a minimal one just to load the tested SpringMVC controller being tested. Components are not autowired, you need to mock them. – alexbt Jan 13 '17 at 18:23
  • 2
    You should avoid field injection and replace it with constructor injection. This makes testing your code way easier. Also, do you *really* need Spring in your tests? If not, inject the dependencies manually into the class to test either via Mockitos `Whitebox.setInternalState(...)` or Springs `ReflectionUtils.setField(...)` methods (if no constructor injection is available) – Roman Vottner Jan 13 '17 at 18:26
  • This was part of a spring boot app, so i added WebMvcTest . Now after the explanations i have removed it. Thanks! – Umar Jan 13 '17 at 19:36

2 Answers2

2

You may want to use Mockito's runner instead of Spring (from what I see, you do not need Spring's context at all):

@RunWith(MockitoJUnitRunner.class)
public class SubscriptionServiceTest {

    @InjectMocks
    private AuthErrorResponseWriter authErrorResponseWriter;

    @Mock
    TransResponse svcResponse;

    @Mock
    HttpServletResponse responseMock;

    ....

    authErrorWriter.writeResponse(responseMock);
alexbt
  • 16,415
  • 6
  • 78
  • 87
1

svcResponse is null, even though i have mocked it.

No, you haven't mocked it. This is what you are doing in your code:

TransResponse mockResponse = new TransResponse();
mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());

What you should be doing is something like this:

@Mock
private TransResponse mockResponse;

You'll have to inject the mocks in the Target class like this:

@InjectMocks
private AuthErrorResponseWriter authErrorWriter;

And use, authErrorWriter instead of creating a new instance of the class in your test.

And then you can do something like this:

Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
user2004685
  • 9,548
  • 5
  • 37
  • 54
  • To piggy back on this (since its partially correct), you need to inject your mocks into the Target class so spring will use your mock during the Autowiring process. – Ryan S Jan 13 '17 at 18:27
  • @RyanS You are absolutely correct. I was adding that part to my answer. – user2004685 Jan 13 '17 at 18:29
  • Even better, don't use `@InjectMocks` and avoid this mess. Inject dependencies via constructor. – Christopher Schneider Jan 13 '17 at 18:34
  • please demonstrate @ChristopherSchneider – Siddharth Kumar Jan 13 '17 at 18:36
  • I really don't see any reason not to use constructor injection. Constructors could be large, but that's more of a hint that your class is too big. Here's a good answer with an example of constructor injection and using it when unit testing. http://stackoverflow.com/a/16427047/3059385 – Christopher Schneider Jan 13 '17 at 18:51
  • @ChristopherSchneider There is nothing like wrong or right here. The framework provides you both the ways of doing it. It simply depends on the design. There is nothing wrong in injecting the mocks this way! If the user is willing to change the class then yeah, constructor, is another way of doing it. – user2004685 Jan 13 '17 at 18:54
  • I never said it was right or wrong. Though I firmly believe one way is better. – Christopher Schneider Jan 13 '17 at 18:55
  • @ChristopherSchneider Why do you think it's *better* to do it with a constructor and not this way? – user2004685 Jan 13 '17 at 18:57
  • I'm not sure why you are so defensive... This article (written by someone else) sums up my opinions on the matter: http://olivergierke.de/2013/11/why-field-injection-is-evil/ – Christopher Schneider Jan 13 '17 at 19:20
  • It worked after using the above approach. I used @InjectMocks for now. Will also looked into injecting dependencies via constructor. – Umar Jan 13 '17 at 19:37