Java springboot:将测试中的bean注入web环境

Java springboot:将测试中的bean注入web环境,java,spring,spring-boot,junit,junit5,Java,Spring,Spring Boot,Junit,Junit5,我正在为我的示例应用程序构建一些集成测试,我想知道是否可以在测试本身中创建一些测试数据,然后将其注入正在运行的服务器。我不喜欢模拟我的数据,因为我希望我的测试在整个堆栈中运行 我知道Spring引导文档说服务器和测试在两个独立的线程中运行,但是是否可以通过相同的上下文 到目前为止,我掌握的代码是: @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RA

我正在为我的示例应用程序构建一些集成测试,我想知道是否可以在测试本身中创建一些测试数据,然后将其注入正在运行的服务器。我不喜欢模拟我的数据,因为我希望我的测试在整个堆栈中运行

我知道Spring引导文档说服务器和测试在两个独立的线程中运行,但是是否可以通过相同的上下文

到目前为止,我掌握的代码是:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ArtistResourceTests {
    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private ArtistRepository artistRepository;

    @Test
    @Transactional
    public void listReturnsArtists() {
        Artist artist = new DataFactory().getArtist();
        this.artistRepository.save(artist);

        ParameterizedTypeReference<List<Artist>> artistTypeDefinition = new ParameterizedTypeReference<List<Artist>>() {};
        ResponseEntity<List<Artist>> response = this.restTemplate.exchange("/artists", HttpMethod.GET, null, artistTypeDefinition);

        assertEquals(1, response.getBody().size());
    }
}
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.RANDOM\u端口)
公共类源代码测试{
@自动连线
私有TestRestTemplate;
@自动连线
私人艺术品收藏;
@试验
@交易的
公营机构{
艺术家=新数据工厂().getArtist();
这是一位艺术家;
ParameteredTypeReference artistTypeDefinition=新的ParameteredTypeReference(){};
ResponseEntity response=this.restTemplate.exchange(“/artists”,HttpMethod.GET,null,artistTypeDefinition);
assertEquals(1,response.getBody().size());
}
}

但这将返回0个结果,而不是1个结果。

我认为您不会与远程运行的服务器交互

SpringBootTest
annotation在测试内部本地启动整个微服务。 否则,如果您的测试只是对远程服务器的一系列调用,那么您实际上不需要
@SpringBootTest
(并且根本不需要springboot:)

因此,在测试内部有一个应用程序上下文。 现在您要问的是如何预填充数据。这太宽泛了,因为您没有指定数据存储的确切位置以及涉及哪些数据持久性层(RDBMS、Elasticsearch、Mongo等等)

一种可能的通用方法是使用,它可以有一个方法
beforeTestMethod

启动应用程序上下文,这样您就可以真正以自定义方式准备数据,并将其保存到您选择的数据库中(通过将DAO注入侦听器或其他方式)

如果使用Flyway,另一个有趣的方法是在
src/test/resources/data
文件夹中提供迁移,以便Flyway在测试期间自动执行迁移

更新

注释说明使用了H2 DB,在这种情况下,假设数据源配置正确并且确实提供了到H2的连接,最简单的方法是运行带有数据插入的SQL脚本:

@Sql(scripts = {"/scripts/populateData1.sql", ..., "/scripts/populate_data_N.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public void myTest() {
...
}

现在,如果你必须与

this.artistRepository.save(artist);
那么spring就不关心线程了。只要“数据”是bean(或资源),它就可以注入任何数据,因为您使用的是对象(艺术家),所以它必须是bean

因此,使用bean
Artist
创建
TestConfiguration
,确保其与test位于同一个包中(以便spring引导测试扫描过程将加载配置),并像往常一样使用@Autowired将其注入测试:

@TestConfiguration
public class ArtistsConfiguration {

   @Bean 
   public Artist artistForTests() {
       return new Artist(...);
   }
}


@Import(ArtistsConfiguration.class)
@SpringBootTest
public class MyTest {

   @Autowired
   private Artist artist;

   ....
}

您可以使用@ContextConfiguration,如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
@ContextConfiguration(locations = {"/testApplicationContext.xml"})
public class TestGetPerson {

    @Autowired
    private PersonService personService;
...
然后在testApplicationContext中指定Spring应扫描的包:

<context:component-scan base-package="nl.example.server.service"/>
<context:component-scan base-package="nl.example.server.test.db"/>
这不会在单独的服务器上运行测试数据,但会在尽可能紧密地整合应用程序环境的环境中运行测试


关于您的问题,您可以创建自己的mock,将测试数据注入到您喜欢的任何组件中,而不是使用Mockito mock

据我所知,它不是某种“远程运行的服务器”。
@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.RANDOM_PORT)
只是在一个单独的线程中启动应用程序,并在“随机端口”上侦听,我的问题是“如何将存储库的测试(数据工厂的艺术家)中的测试数据注入该线程?”。我正在使用H2数据库。请查看更新,希望它解决了问号,谢谢你的回答,但存储库已经是一个bean。它是可用的。问题是目前内存中有2个数据库。一个是测试,另一个是服务器本身。更多信息:显然是
@Transactional
造成了问题。就我的例子来说,这就足够了,但是当使用postgres或其他东西时,这会导致问题(因为数据是持久的),但问题仍然存在,是否可以以简洁的方式回滚单独的线程?嘿,Christine,谢谢你的回答,但并没有真正回答我的问题。我正在使用这个设置:
您的测试是@Transactional的,它在每个测试方法结束时回滚事务。使用随机_端口或定义的_端口的这种安排隐式地提供了一个真正的servlet环境,HTTP客户机和服务器在单独的线程中运行,从而在单独的事务中运行。在这种情况下,在服务器上启动的任何事务都不会回滚
@Configuration
@Profile("test")
public class CustomerManagerConfig {
@Bean("customerManager")

    public CustomerManager customerManager() {
        return Mockito.mock(CustomerManager.class);
    }
}