Unit testing 单元测试中未触发弹簧特性

Unit testing 单元测试中未触发弹簧特性,unit-testing,spring-mvc,aop,Unit Testing,Spring Mvc,Aop,好的,我们说的是Spring(3.2.0)MVC 我们定义了一个切入点,可以“围绕”注释触发,如下所示: @Around("@annotation(MyAnnotation)") public void someFunction() { } 在控制器中,我们有: @Controller @Component @RequestMapping("/somepath") public class MyController { @Autowired private MyServic

好的,我们说的是Spring(3.2.0)MVC

我们定义了一个切入点,可以“围绕”注释触发,如下所示:

@Around("@annotation(MyAnnotation)")
public void someFunction() {

}
在控制器中,我们有:

@Controller
@Component
@RequestMapping("/somepath")
public class MyController {

    @Autowired
    private MyService service;

    ...

    @MyAnnotation
    @RequestMapping(value = "/myendpoint", method = RequestMethod.POST, produces = "application/json")
    @ResponseBody
    public Object myEndpoint(@RequestBody MyRequestObject requestObject, HttpServletRequest request, HttpServletResponse response) {
        ...
        return service.doSomething(requestObject);
    }         
}
然后我们有一个单元测试,如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"../path/to/applicationContext.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {

    private MockMvc mockMvc;

    @InjectMocks
    private MyController controller;

    @Mock
    private MyService myService;    

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }


    @Test
    public void myTest() {
        MyRequest request = new MyRequest();
        MyResponse response = new MyResponse();
        String expectedValue = "foobar";

        Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");

        String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());

        builder.content(request);
        builder.contentType(MediaType.APPLICATION_JSON);

        mockMvc.perform(builder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));

        Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"testContext.xml","../path/to/applicationContext.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private MyService myService;    

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void myTest() {
        MyRequest request = new MyRequest();
        MyResponse response = new MyResponse();
        String expectedValue = "foobar";

        Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");

        String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());

        builder.content(request);
        builder.contentType(MediaType.APPLICATION_JSON);

        mockMvc.perform(builder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));

        Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
    }
}
测试运行正常,但围绕注释(MyAnnotation)定义的方面不会执行。当端点由实际请求触发时(例如,在servlet容器中运行时),这会很好地执行,但在测试中运行时不会触发

这是MockMvc的一个特殊“特性”,它不会触发方面吗

仅供参考,我们的applicationContext.xml配置为:

<aop:aspectj-autoproxy/>

正如我提到的,这些方面在现实中确实有效,只是在测试中没有

有人知道如何激发这些方面吗


谢谢

好的。。因此,解决方案最终从。。你猜对了。。阅读文档:/

此外,您可以通过Spring配置将模拟服务注入控制器中,以便集中精力测试web层

最终的解决方案如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"../path/to/applicationContext.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {

    private MockMvc mockMvc;

    @InjectMocks
    private MyController controller;

    @Mock
    private MyService myService;    

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }


    @Test
    public void myTest() {
        MyRequest request = new MyRequest();
        MyResponse response = new MyResponse();
        String expectedValue = "foobar";

        Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");

        String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());

        builder.content(request);
        builder.contentType(MediaType.APPLICATION_JSON);

        mockMvc.perform(builder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));

        Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"testContext.xml","../path/to/applicationContext.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private MyService myService;    

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void myTest() {
        MyRequest request = new MyRequest();
        MyResponse response = new MyResponse();
        String expectedValue = "foobar";

        Mockito.when(myService.doSomething((MyRequest) Mockito.any())).thenReturn(response);

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/myendpoint");

        String request = IOUtils.toString(context.getResource("classpath:/request.json").getURI());

        builder.content(request);
        builder.contentType(MediaType.APPLICATION_JSON);

        mockMvc.perform(builder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.someKey").value(expectedValue));

        Mockito.verify(myService, Mockito.times(1)).doSomething((MyRequest) Mockito.any());
    }
}
然后,您只需为此测试定义一个上下文文件
testContext.xml
,该文件具有服务对象的模拟:

<bean id="myService" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.mypackage.MyService"/>
</bean>

我有一个类似的设置,使用MockMVC通过层执行集成测试。在我重命名注释的包之前,注释工作正常,没有任何额外的代码。它停止工作了。注释将不会执行。在我的头撞了一会儿之后,我意识到我已经解决了这个问题:组件扫描注释的包

在应用程序的一个配置类文件中可能有类似的语句。您需要在测试类中重复这些类型的声明

@SpringBootTest
@AutoConfigureMockMvc
@ComponentScan("annotation.package")
public class MyControllerTest {

}

更新。如果我从
standaloneSetup
更改为
webAppContextSetup
,则会触发注释,但很明显,在这种情况下,被调用的控制器不是被注入模拟的控制器,因此它使用实际的服务类执行。因此,问题要么是使用
standaloneSetup
触发注释,要么是使用
webAppContextSetup
进一步更新注入mock。看起来,即使手动设置被测控制器的字段也失败了。类似这样:
MyController-controller=wac.getBean(MyController.class)返回与处理请求时使用的实例不同的实例。我看到
MyController
的构造函数被调用了两次,这看起来不再像是单元测试了。这更像是一种整合test@ssgao取决于你认为是一个单元测试:TX@ Jason