首页 文章

在Integration测试中覆盖bean

提问于
浏览
17

对于我的Spring-Boot应用程序,我通过@Configuration文件提供了RestTemplate,因此我可以添加合理的默认值(ex Timeouts) . 对于我的集成测试,我想模拟RestTemplate,因为我不想连接到外部服务 - 我知道期望的响应 . 我尝试在集成测试包中提供不同的实现,希望后者将覆盖实际的实现,但是反过来检查日志:真正的实现覆盖了测试 .

How can I make sure the one from the TestConfig is the one used?

这是我的配置文件:

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

3 回答

  • 13

    从Spring Boot 1.4.x开始,有一个选项可以使用 @MockBean 注释来伪造Spring bean .

    Reaction on comment:

    要保持缓存中的上下文不使用 @DirtiesContext ,但使用 @ContextConfiguration(name = "contextWithFakeBean") 并且它将创建单独的上下文,同时它将在缓存中保留默认上下文 . Spring会在缓存中保留两者(或者你有多少个上下文) .

    我们的构建就是这种方式,大多数测试都使用默认的非堆积配置,但是我们有4-5个假冒 beans 类测试 . 默认上下文很好地重用

  • 12

    1.您可以使用 @Primary 注释:

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

    顺便说一下,我写了blog post about faking Spring bean

    但我建议你去看看Spring RestTemplate testing support . 这将是一个简单的例子: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);
      }
    

    更多例子可以在my Github repo here找到

  • 2

    我用问题解决了问题

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

    代替

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

    在我的情况下,测试不在与App相同的包中 . 所以我需要明确指定AppConfiguration.class(或App.class) . 如果你在测试中使用相同的包,那么我猜你可以写

    @SpringBootTest(classes = AppTestConfiguration.class)
    

    而不是(不工作)

    @Import(AppTestConfiguration.class );
    

    看到这是如此不同,这是非常有意义的 . 也许有人可以解释这一点 . 到目前为止,我找不到任何好的回答 . 您可能会想,如果存在 @SpringBootTests ,则不会拾取 @Import(...) ,但是在日志中会显示覆盖的bean . 但只是错误的方式 .

    顺便说一句,使用 @TestConfiguration 而不是 @Configuration 也没有区别 .

相关问题