web-dev-qa-db-ja.com

Spring MVC Controllers Unit Testが@ControllerAdviceを呼び出さない

アプリケーションにコントローラーのセットと、これらのコントローラーのそれぞれで使用される特定のデータ要素を設定する@ControllerAdviceとして注釈が付けられたクラスがあります。私はSpring MVC 3.2を使用しており、これらのコントローラーにJunitsを使用しています。 Junitを実行すると、コントロールはControllerAdviceクラスに移動しません。そこで、アプリをTomcatにデプロイし、ブラウザー経由でリクエストを送信すると正常に動作します。

ご意見をお聞かせください。

46
Arun

@ eugene-toおよび他の同様の回答を使用した後、 here Springで制限を見つけ、問題を提起しました: https://jira.spring.io/browse/SPR-12751

その結果、Springテストでは、4.2でビルダーに_@ControllerAdvice_クラスを登録する機能が導入されました。 Spring Bootを使用している場合は、1.3.0以降が必要です。

この改善により、スタンドアロンセットアップを使用している場合、1つ以上のControllerAdviceインスタンスを次のように設定できます。

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

注:名前setControllerAdvice()はすぐにはわかりませんが、var-を持っているため、多くのインスタンスを渡すことができますargs署名。

84
Matt Byrne

@ExceptionHandlerアノテーションが付けられたメソッドを持つ@ControllerAdviceアノテーションが付けられたクラスMyControllerAdviceがあるとします。 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;
}
41
Eugene To

@ControllerAdviceアノテーションが付けられたExceptionHandlerをテストしようとすると、同様の問題が発生しました。私の場合、テストクラスの@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構成でテストに合格しました。

18
tunguski

これは私のために働いています

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

    }

}
7
Bikesh M

私はかなり長い間、同じことで苦労してきました。掘り下げた後、最良のリファレンスはSpringのドキュメントでした:

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

要するに、コントローラーとそのメソッドを単純にテストする場合、 'standaloneSetup'メソッドを使用して、簡単なSpring MVC構成を作成できます。これには、notに@ControllerAdviceで注釈を付けたエラーハンドラが含まれます。

private MockMvc mockMvc;

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

// ...

doesがエラーハンドラを含む、より完全な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;

    // ...

}
6
Neil D

@tunguskiサンプルコードは機能しますが、物事がどのように機能するかを理解することは有益です。これは、物事を設定する1つの方法にすぎません。

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

そして、次はテスト用のスプリング構成です

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

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が無視されました。コードをモックを自動配線するように変更すると、問題が修正されました。

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

  @AutowiredMock
  FooService FooService

  @Autowired
  MockMvc mockMvc
0
Mustafa

テストでasyncDispatchを使用する必要があると思います。非同期コントローラーでは、通常のテストフレームワークが壊れています。

アプローチを試してください: https://github.com/spring-projects/spring-framework/blob/master/spring-test/src/test/Java/org/springframework/test/web/servlet/samples /standalone/AsyncTests.Java

0
Eddy

特定の回答を期待する前に、より多くの情報と、おそらく実際のコードや設定ファイルを提供する必要があります。とは言っても、提供された小さな情報に基づいて、注釈付きBeanがロードされていないように聞こえます。

テストapplicationContext.xml(または、使用している場合は同等のspring configファイル)に次を追加してみてください。

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

または、テストクラスの前に次の注釈を含めることにより、テスト内でコンテキストを「手動で」ロードする必要がある場合があります。

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

がんばろう!

0
Liam