Подтвердить что ты не робот

Spring Контроллеры MVC Unit Test, не вызывающие @ControllerAdvice

У меня есть набор контроллеров в приложении и класс, аннотированный как @ControllerAdvice, который устанавливает определенные элементы данных, которые используются в каждом из этих контроллеров. Я использую Spring MVC 3.2 и имею Junits для этих контроллеров. Когда я запускаю Junit, элемент управления не идет в класс ControllerAdvice, если он работает отлично, если я развертываю приложение в Tomcat и отправляю запрос через браузер.

Любые мысли, пожалуйста?

4b9b3361

Ответ 1

После использования ответа от @eugene-to и другого подобного здесь я нашел ограничения и поднял проблему на Spring: https://jira.spring.io/browse/SPR-12751

В результате тест Spring ввел возможность зарегистрировать классы @ControllerAdvice в построителе в 4.2. Если вы используете Spring Boot, вам понадобится 1.3.0 или новее.

При этом улучшении, если вы используете автономную настройку, вы можете установить один или несколько экземпляров ControllerAdvice, например:

mockMvc = MockMvcBuilders.standaloneSetup(yourController)
            .setControllerAdvice(new YourControllerAdvice())
            .build();

Примечание. имя setControllerAdvice() может не сделать это сразу же очевидным, но вы можете передать ему много экземпляров, поскольку оно имеет подпись var-args.

Ответ 2

Предположим, что у вас есть класс MyControllerAdvice, аннотированный с помощью @ControllerAdvice, который имеет методы, аннотированные с помощью @ExceptionHandler. Для MockMvc вы можете легко добавить этот класс в качестве преобразователя исключений.

@Before
public void beforeTest() {
    MockMvc mockMvc = standaloneSetup(myControllers)
        .setHandlerExceptionResolvers(createExceptionResolver())
        .build();
}

private ExceptionHandlerExceptionResolver createExceptionResolver() {
    ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
            Method method = new ExceptionHandlerMethodResolver(MyControllerAdvice.class).resolveMethod(exception);
            return new ServletInvocableHandlerMethod(new MyControllerAdvice(), method);
        }
    };
    exceptionResolver.afterPropertiesSet();
    return exceptionResolver;
}

Ответ 3

У меня была аналогичная проблема при попытке протестировать ExceptionHandler с аннотацией с помощью @ControllerAdvice. В моем случае мне пришлось добавить файл @Configuration с аннотацией @EnableWebMvc к @ContextConfiguration в тестовом классе.

Итак, мой тест выглядел так:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {
  RestProcessingExceptionHandler.class,
  TestConfiguration.class,
  RestProcessingExceptionThrowingController.class })
public class TestRestProcessingExceptionHandler {


  private MockMvc mockMvc;
  @Autowired
  WebApplicationContext wac;


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


  @Configuration
  // !!! this is very important - conf with this annotation 
  //     must be included in @ContextConfiguration
  @EnableWebMvc
  public static class TestConfiguration { }


  @Controller
  @RequestMapping("/tests")
  public static class RestProcessingExceptionThrowingController {


    @RequestMapping(value = "/exception", method = GET)
    public @ResponseBody String find() {
      throw new RestProcessingException("global_error_test");
    }
  }


  @Test
  public void testHandleException() throws Exception {
    mockMvc.perform(get("/tests/exception"))
        .andExpect(new ResultMatcher() {


          @Override
          public void match(MvcResult result) throws Exception {
            result.getResponse().getContentAsString().contains("global_error_test");
          }
        })
        .andExpect(status().isBadRequest());
  }
}

С настройкой @EnableWebMvc мой тест прошел.

Ответ 4

Я боролся с тем же самым в течение достаточно долгого времени. После долгих поисков лучшим справочником была документация Spring:

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework

Короче говоря, если вы просто тестируете контроллер и его методы, вы можете использовать метод standaloneSetup, который создает простую конфигурацию Spring MVC. Это не будет включать ваш обработчик ошибок, который вы аннотируете с помощью @ControllerAdvice.

private MockMvc mockMvc;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}

// ...

Чтобы создать более полную конфигурацию Spring MVC, которая содержит ваш обработчик ошибок, вы должны использовать следующую настройку:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("test-servlet-context.xml")
public class AccountTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Autowired
    private AccountService accountService;

    // ...

}

Ответ 5

Это работает для меня

public class MyGlobalExceptionHandlerTest {

    private MockMvc mockMvc;

    @Mock
    HealthController healthController;

    @BeforeTest
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(healthController).setControllerAdvice(new GlobalExceptionHandler())
            .build();
    }

    @Test(groups = { "services" })
    public void testGlobalExceptionHandlerError() throws Exception {

        Mockito.when(healthController.health()).thenThrow(new RuntimeException("Unexpected Exception"));

        mockMvc.perform(get("/health")).andExpect(status().is(500)).andReturn();

    }

}

Ответ 6

@tunguski пример кода работает, но он платит, чтобы понять, как все работает. Это всего лишь один способ установить ситуацию.

@EnableWebMvc эквивалентен следующему в конфигурационном файле spring

<mvc:annotation-driven />

По сути, для работы вам необходимо инициализировать spring Mvc и загрузить все ваши контроллеры и ссылки bean. Таким образом, следующая допустимая установка, а также альтернативный

Ниже описано, как настроить тестовый класс

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath: "classpath:test-context.xml" })
    @WebAppConfiguration    
    public class BaseTest {

        @Autowired
        WebApplicationContext wac;

        private MockMvc mockMvc;

        @Before
        public void setUp()  {
            mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        }
    }

И следующая конфигурация spring для теста

<mvc:annotation-driven />
<context:component-scan base-package="com.base.package.controllers" />

Ответ 7

Вам нужно будет предоставить дополнительную информацию и, возможно, некоторые фактические файлы кода и/или конфигурации, прежде чем вы сможете ожидать конкретных ответов. При этом, основываясь на том, что вы предоставили, похоже, что аннотированный bean не загружается.

Попробуйте добавить в тестовый файл applicationContext.xml(или эквивалентный файл конфигурации spring), если вы используете его.

<context:component-scan base-package="com.example.path.to.package" />

В качестве альтернативы вам может потребоваться "вручную" загрузить контексты в тестах, включив следующие аннотации перед вашим тестовым классом:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")

Удачи!

Ответ 8

Я столкнулся с этой проблемой при написании тестов контроллера с помощью spock (groovy). Мой тестовый класс изначально был написан следующим образом:

@AutoConfigureMockMvc(secure = false)
@SpringBootTest
@Category(RestTest)
class FooControllerTest extends Specification {
  def fooService = Mock(FooService)
  def underTest = new FooController(FooService)
  def mockMvc = MockMvcBuilders.standaloneSetup(underTest).build()
....
}

Это привело к игнорированию ControllerAdvice. Изменение кода на Autowire mocks устраняет проблему.

@AutoConfigureMockMvc(secure = false)
@SpringBootTest
@Category(RestTest)
class FooControllerTest extends Specification {

  @AutowiredMock
  FooService FooService

  @Autowired
  MockMvc mockMvc