web-dev-qa-db-ja.com

統合テストでBeanをオーバーライドする

私のSpring-Bootアプリでは、@ Configurationファイルを使用してRestTemplateを提供しているため、適切なデフォルト(タイムアウトなど)を追加できます。統合テストでは、外部サービスに接続したくないので、RestTemplateをモックしたいと思います-どのような応答が期待されるかはわかっています。後者が実際の実装をオーバーライドすることを期待して、統合テストパッケージに別の実装を提供しようとしましたが、ログを確認することは逆です:実際の実装はテスト実装をオーバーライドします。

TestConfigからのものが使用されていることを確認するにはどうすればよいですか?

これは私の設定ファイルです:

@Configuration
public class RestTemplateProvider {

    private static final int DEFAULT_SERVICE_TIMEOUT = 5_000;

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate(buildClientConfigurationFactory());
    }

    private ClientHttpRequestFactory buildClientConfigurationFactory() {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT);
        factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT);
        return factory;
    }
}

統合テスト:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@ActiveProfiles("it")
public abstract class IntegrationTest {}

TestConfigurationクラス:

@Configuration
@Import({Application.class, MockRestTemplateConfiguration.class})
public class TestConfiguration {}

そして最後にMockRestTemplateConfiguration

@Configuration
public class MockRestTemplateConfiguration {

    @Bean
    public RestTemplate restTemplate() {
        return Mockito.mock(RestTemplate.class)
    }
}
30
mvlupan

Spring Boot 1.4.x以降、_@MockBean_注釈を使用してSpring Beanを偽造するオプションがあります。

コメントに対する反応:

コンテキストをキャッシュに保持するには、_@DirtiesContext_を使用せず、@ContextConfiguration(name = "contextWithFakeBean")を使用します。個別のコンテキストが作成されますが、デフォルトのコンテキストはキャッシュに保持されます。 Springは両方(またはコンテキストの数)をキャッシュに保持します。

ビルドはこのように行われ、ほとんどのテストではデフォルトの非汚染構成が使用されますが、Beanを偽造するテストは4〜5個あります。デフォルトのコンテキストはうまく再利用されます

22
luboskrnac

1. @Primaryアノテーションを使用できます:

@Configuration
public class MockRestTemplateConfiguration {

    @Bean
    @Primary
    public RestTemplate restTemplate() {
        return Mockito.mock(RestTemplate.class)
    }
}

ところで、私は Spring Beanの偽造に関するブログ記事 を書きました。

2.しかし、 Spring RestTemplateテストのサポート をご覧になることをお勧めします。これは簡単な例です:private MockRestServiceServer mockServer;

  @Autowired
  private RestTemplate restTemplate;

  @Autowired
  private UsersClient usersClient;

  @BeforeMethod
  public void init() {
    mockServer = MockRestServiceServer.createServer(restTemplate);
  }

  @Test
  public void testSingleGet() throws Exception {
    // GIVEN
    int testingIdentifier = 0;
    mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier))
      .andExpect(method(HttpMethod.GET))
      .andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON));


    // WHEN
    User user = usersClient.getUser(testingIdentifier);

    // THEN
    mockServer.verify();
    assertEquals(user.getName(), USER0_NAME);
    assertEquals(user.getEmail(), USER0_EMAIL);
  }

より多くの例は 私のGithubレポはこちら にあります。

23
luboskrnac

もう少し深く、2番目の答えを参照

を使用して問題を解決しました

_@SpringBootTest(classes = {AppConfiguration.class, AppTestConfiguration.class})
_

の代わりに

_@Import({ AppConfiguration.class, AppTestConfiguration.class });
_

私の場合、テストはアプリと同じパッケージに入っていません。そのため、明示的にAppConfiguration.class(またはApp.class)を指定する必要があります。テストで同じパッケージを使用する場合、私はあなたが書くことができると思うよりも

_@SpringBootTest(classes = AppTestConfiguration.class)
_

代わりに(動作していません)

_@Import(AppTestConfiguration.class );
_

これが非常に異なることを確認するために結構結構です。これを説明できる人がいるかもしれません。今まで良い答えが見つかりませんでした。 _@SpringBootTests_が存在する場合、@Import(...)は選択されないと思うかもしれませんが、ログにオーバーライドするBeanが表示されます。しかし、間違った方法です。

ちなみに、_@TestConfiguration_の代わりに_@Configuration_を使用しても違いはありません。

10
Torsten

構成の問題は、テスト構成に@Configurationを使用していることです。これにより、メイン構成が置き換えられます。代わりに、@TestConfigurationを使用して、メイン構成を追加(上書き)します。

46.3.2テスト構成の検出

プライマリ設定をカスタマイズする場合は、ネストされた@TestConfigurationクラスを使用できます。アプリケーションのプライマリ設定の代わりに使用されるネストされた@Configurationクラスとは異なり、ネストされた@TestConfigurationクラスはアプリケーションのプライマリ設定に加えて使用されます。

SpringBootを使用した例:

メインクラス

@SpringBootApplication() // Will scan for @Components and @Configs in package tree
public class Main{
}

メイン設定

@Configuration
public void AppConfig() { 
    // Define any beans
}

テスト構成

@TestConfiguration
public void AppTestConfig(){
    // override beans for testing
} 

テストクラス

@RunWith(SpringRunner.class)
@Import(AppTestConfig.class)
@SpringBootTest
public void AppTest() {
    // use @MockBean if you like
}

注:オーバーライドするBeanを含め、すべてのBeanが作成されることに注意してください。 @Profileをインスタンス化しない場合は、@Configurationを使用します。

8
Torsten

this answerをそのスレッドで提供されている他の項目と一緒にチェックします。 Spring Boot 2.XでBeanをオーバーライドします。このオプションはデフォルトで無効になっています。また、そのパスを取ることにした場合のBean Definition DSLの使用方法についてのアイデアもあります。

0
yuranos87

@MockBeanとOPが使用するBeanオーバーライドは、2つの補完的なアプローチです。

@MockBeanを使用してモックを作成し、実際の実装を忘れます。一般的に、テストしているクラスが依存しているBeanこれらのBeanを統合でテストしたくない
Springはデフォルトでそれらをnullにしますが、テストを実行するためにそれらの最小限の動作をモックします。

@WebMvcTestは、レイヤー全体をテストしたくないため、この戦略を非常に頻繁に必要とします。また、@SpringBootTestは、テスト構成でBean構成のサブセットのみを指定する場合も必要になる場合があります。

一方、可能な限り多くの実際のコンポーネントで統合テストを実行したい場合があります。そのため、@MockBeanを使用したくないが、動作、依存関係を少しオーバーライドしたい、または新しいスコープを定義したいBeanの場合、この場合、従うアプローチはBeanのオーバーライドです。

@SpringBootTest({"spring.main.allow-bean-definition-overriding=true"})
@Import(FooTest.OverrideBean.class)
public class FooTest{    

    @Test
    public void getFoo() throws Exception {
        // ...     
    }

    @TestConfiguration
    public static class OverrideBean {    

        // change the bean scope to SINGLETON
        @Bean
        @Scope(ConfigurableBeanFactory.SINGLETON)
        public Bar bar() {
             return new Bar();
        }

        // use a stub for a bean 
        @Bean
        public FooBar BarFoo() {
             return new BarFooStub();
        }

        // use a stub for the dependency of a bean 
        @Bean
        public FooBar fooBar() {
             return new FooBar(new StubDependency());
        }

    }
}
0
davidxxx