Testcontainers是一个 Java 库,可在Docker容器内启动服务、运行测试并最终销毁容器。您无需担心任何事情,框架可以完成这项工作。只要确保你已经安装了 Docker,然后你就可以开始了。该库支持数十种不同的数据库和模块(PostgreSQL、MySQL、MongoDB、Kafka、Elasticsearch、Nginx、Toxiproxy等)。即使您没有找到您需要的那个,您也可以使用通用容器创建。
第一步是添加所需的依赖项:
testImplementation 'org.testcontainers:junit-jupiter' testImplementation 'org.testcontainers:postgresql' runtimeOnly 'org.postgresql:postgresql'
|
然后我们需要在src/test/resources配置文件配置.这样Spring能够通过区分不同的配置文件的。配置文件名称如application-PROFILE_NAME.yml. 例如,application-test-containers.yml仅当test-containers配置文件处于活动状态时才应用命名的文件。
spring: datasource: url: jdbc:tc:postgresql:9.6.8:///test_database username: user password: password jpa: hibernate: ddl-auto: create
|
JDBC 连接字符串中的后缀是啥?这就是JUnit 5和测试容器的结合所带来的魔力。问题是您根本不需要任何编程配置!当框架看到url包含tc后缀时,它会在内部运行所有必要的 Docker 命令。您可以在此处找到更多示例。
我们设置spring.jpa.hibernate.ddl-auto=create属性,以便根据实体类的定义自动创建数据库模式。
现在让我们PersonCreateService.createFamily看一下方法及其 H2 测试。
@Service @RequiredArgsConstructor public class PersonCreateServiceImpl implements PersonCreateService {
private final PersonValidateService personValidateService; private final PersonRepository personRepository;
@Override @Transactional public List<PersonDTO> createFamily(Iterable<String> firstNames, String lastName) { final var people = new ArrayList<PersonDTO>(); firstNames.forEach(firstName -> people.add(createPerson(firstName, lastName))); return people; } @Override @Transactional public PersonDTO createPerson(String firstName, String lastName) { personValidateService.checkUserCreation(firstName, lastName); final var createdPerson = personRepository.saveAndFlush( new Person() .setFirstName(firstName) .setLastName(lastName) ); return DTOConverters.toPersonDTO(createdPerson); } } @SpringBootTest(webEnvironment = WebEnvironment.NONE) @AutoConfigureTestDatabase class PersonCreateServiceImplSpringBootTest {
@Autowired private PersonRepository personRepository; @MockBean private PersonValidateService personValidateService; @Autowired private PersonCreateService personCreateService;
@BeforeEach void init() { personRepository.deleteAll(); }
@Test void shouldCreateOnePerson() { final var people = personCreateService.createFamily( List.of("Simon"), "Kirekov" ); assertEquals(1, people.size()); final var person = people.get(0); assertEquals("Simon", person.getFirstName()); assertEquals("Kirekov", person.getLastName()); assertTrue(person.getDateCreated().isBefore(ZonedDateTime.now())); }
@Test void shouldRollbackIfAnyUserIsNotValidated() { doThrow(new ValidationFailedException("")) .when(personValidateService) .checkUserCreation("John", "Brown"); assertThrows(ValidationFailedException.class, () -> personCreateService.createFamily( List.of("Matilda", "Vasya", "John"), "Brown" )); assertEquals(0, personRepository.count()); } }
|
我们如何使用 Testcontainers PostgreSQL实例运行测试?我们所要做的就是添加两个注释。@AutoConfigureTestDatabase虽然应该删除。
- @ActiveProfiles("test-containers")— 激活test-containers配置文件,以便 Spring 可以读取前面描述的配置文件
- @Testcontainers— 告诉在Docker 中自动运行 PostgreSQL 实例
@SpringBootTest(webEnvironment = WebEnvironment.NONE) @Testcontainers @ActiveProfiles("test-containers") class PersonCreateServiceImplTestContainers {
@Autowired private PersonRepository personRepository; @MockBean private PersonValidateService personValidateService; @Autowired private PersonCreateService personCreateService;
@BeforeEach void init() { personRepository.deleteAll(); }
@Test void shouldCreateOnePerson() { final var people = personCreateService.createFamily( List.of("Simon"), "Kirekov" ); assertEquals(1, people.size()); final var person = people.get(0); assertEquals("Simon", person.getFirstName()); assertEquals("Kirekov", person.getLastName()); assertTrue(person.getDateCreated().isBefore(ZonedDateTime.now())); }
@Test void shouldRollbackIfAnyUserIsNotValidated() { doThrow(new ValidationFailedException("")) .when(personValidateService) .checkUserCreation("John", "Brown"); assertThrows(ValidationFailedException.class, () -> personCreateService.createFamily( List.of("Matilda", "Vasya", "John"), "Brown" )); assertEquals(0, personRepository.count()); } }
|
运行测试成功。
存储库测试
测试存储库层怎么样?让我们再看看 H2 的例子。
@DataJpaTest class PersonRepositoryDataJpaTest {
@Autowired private PersonRepository personRepository;
@Test void shouldReturnAlLastNames() { personRepository.saveAndFlush(new Person().setFirstName("John").setLastName("Brown")); personRepository.saveAndFlush(new Person().setFirstName("Kyle").setLastName("Green")); personRepository.saveAndFlush(new Person().setFirstName("Paul").setLastName("Brown"));
assertEquals(Set.of("Brown", "Green"), personRepository.findAllLastNames()); } }
|
规则相同,但略有不同。@DataJpaTest是类似前面@AutoConfigureTestDatabase的独特注释。默认情况下,此注释用 H2 实例替换任何数据源。因此,我们需要通过添加replace=Replace.NONE属性来覆盖此行为。
@DataJpaTest @Testcontainers @ActiveProfiles("test-containers") @AutoConfigureTestDatabase(replace = Replace.NONE) class PersonRepositoryTestContainers {
@Autowired private PersonRepository personRepository;
@Test void shouldReturnAlLastNames() { personRepository.saveAndFlush(new Person().setFirstName("John").setLastName("Brown")); personRepository.saveAndFlush(new Person().setFirstName("Kyle").setLastName("Green")); personRepository.saveAndFlush(new Person().setFirstName("Paul").setLastName("Brown"));
assertEquals(Set.of("Brown", "Green"), personRepository.findAllLastNames()); } }
|
点击标题看 javarevisited原文