Пружинный аспект не срабатывает в модульном тесте


Хорошо, мы говорим о 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());
    }
}

Тест выполняется нормально, но аспект, определенный вокруг аннотации (MyAnnotation), не выполняется. Это выполняется просто отлично, когда конечная точка инициируется реальным запросом (например, при запуске в контейнере сервлета), но просто не стреляет при запуске в тесте.

Является ли это особой "особенностью" MockMvc, что она не запускает аспекты?

К вашему сведению наш applicationContext.xml настраивается с помощью:

<aop:aspectj-autoproxy/>
И, как я уже упоминал, аспекты действительно работают в реальности, только не в тесте.

Кто-нибудь знает, как заставить эти аспекты выстрелить?

Спасибо!

1 4

1 ответ:

Хорошо.. так что решение в конце концов представилось само собой.. вы сами догадались.. чтение документации: /

Http://docs.spring.io/spring-framework/docs/3.2.0.BUILD-SNAPSHOT/reference/htmlsingle/#spring-mvc-test-framework

Кроме того, вы можете внедрить макетные сервисы в контроллеры через конфигурацию Spring, чтобы оставаться сосредоточенным на тестировании веб-слоя.

Таким образом, окончательное решение выглядит следующим образом:

@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>

Важно, что экземпляр MyService находится @Autowired в тесте, поэтому его можно организовать.

Это позволяет вам имитировать любые экземпляры, которые вам нравятся, независимо от того, находятся ли они в классах обслуживания, аспектах и т. д., Пока вы называете Боб соответствующим образом. Таким образом, в этом случае MyService будет объявлено как:

@Component("myService")
public class MyService {
...