Java 春季及;Mockito:在单元测试中调用实际对象

Java 春季及;Mockito:在单元测试中调用实际对象,java,spring,unit-testing,junit,mockito,Java,Spring,Unit Testing,Junit,Mockito,这是我要测试的课程 public class AuthErrorResponseWriter { @Autowired TransResponse svcResponse; @Override public void writeResponse(HttpServletResponse response) { //Set the Http status respons

这是我要测试的课程

    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);  
            }
    }

}
下面是我编写的单元测试代码

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());

    }

}
当我执行这个Junit时,我得到一个空指针异常

svcsresponse.setMessage(常量.身份验证错误)

svcResponse为null,尽管我已经对它进行了模拟

请有人告诉我,为什么它不拿起模拟对象,并寻找实际的

还有,如果我写Junit是一种合适的方式

svcResponse为null,尽管我已经对它进行了模拟

不,你没有嘲笑它。这就是您在代码中所做的:

TransResponse mockResponse = new TransResponse();
mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());
你应该这样做:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
您必须在目标类中注入模拟,如下所示:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
并使用,
authErrorWriter
,而不是在测试中创建该类的新实例

然后你可以这样做:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
svcResponse为null,尽管我已经对它进行了模拟

不,你没有嘲笑它。这就是您在代码中所做的:

TransResponse mockResponse = new TransResponse();
mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());
你应该这样做:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
您必须在目标类中注入模拟,如下所示:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
并使用,
authErrorWriter
,而不是在测试中创建该类的新实例

然后你可以这样做:

@Mock
private TransResponse mockResponse;
@InjectMocks
private AuthErrorResponseWriter authErrorWriter;
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);

您可能希望使用Mockito的runner而不是Spring(据我所见,您根本不需要Spring的上下文):


您可能希望使用Mockito的runner而不是Spring(据我所见,您根本不需要Spring的上下文):


WebMvcTest
用于测试Spring MVC组件。
AuthErrorResponseWriter
是控制器吗?然后,我看到您自己实例化了
AuthErrorResponseWriter
,所以什么都不做。然后,您根本不模拟
svcsresponse
,因此它是空的。使用
WebMvcTest
时,不会加载实际上下文(仅加载测试的SpringMVC控制器的最小值。组件不是自动连接的,您需要模拟它们。您应该避免字段注入,并将其替换为构造函数注入。这使测试代码更容易。此外,您真的需要在测试中使用Spring吗?如果不需要,请手动将依赖项注入类中以进行测试。)通过Mockitos
Whitebox.setInternalState(…)
或Springs
ReflectionUtils.setField(…)
方法进行测试(如果没有可用的构造函数注入)这是spring boot应用程序的一部分,所以我添加了WebMvcTest。现在在解释之后我删除了它。谢谢!
WebMvcTest
用于测试spring MVC组件。是不是
AuthErrorResponseWriter
控制器?然后,我看到你自己实例化了
AuthErrorResponseWriter
,所以什么都不做。然后,你就没有moc了k
svcsresponse
,因此它是空的。当使用
WebMvcTest
时,不会加载真正的上下文(仅加载测试的SpringMVC控制器的最小值。组件不是自动连接的,您需要模拟它们。您应该避免字段注入,并将其替换为构造函数注入。这使测试代码更容易。此外,您真的需要在测试中使用Spring吗?如果不需要,请手动将依赖项注入类中以进行测试。)通过Mockitos
Whitebox.setInternalState(…)
或Springs
ReflectionUtils.setField(…)
方法进行测试(如果没有可用的构造函数注入),这是spring boot应用程序的一部分,所以我添加了WebMvcTest。现在在解释之后,我已经删除了它。谢谢!因为它部分正确),您需要将您的mock注入目标类,以便spring在自动连接过程中使用您的mock。@RyanS您完全正确。我正在将该部分添加到我的答案中。更好的是,不要使用
@InjectMocks
,避免这种混乱。通过构造函数注入依赖项。请演示@ChristopherSchneiderI真的看不到不使用构造函数注入的任何理由。构造函数可能很大,但这更多的是暗示您的类太大。下面是一个很好的答案,它是一个构造函数注入的示例,并在单元测试时使用它。利用它(因为它部分正确),您需要将您的mock注入目标类,以便spring在自动连接过程中使用您的mock。@RyanS您完全正确。我正在将该部分添加到我的答案中。更好的是,不要使用
@InjectMocks
,避免这种混乱。通过构造函数注入依赖项。请演示@ChristopherSchneiderI真的看不到任何不使用构造函数注入的理由。构造函数可能很大,但这更多的是暗示您的类太大。下面是一个很好的答案,它是一个构造函数注入示例,并在单元测试时使用它。