mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-15 12:25:50 +00:00
Test with test containers and docker compose
This commit is contained in:
parent
cf6d44b045
commit
3c1ef72e6d
7 changed files with 304 additions and 2 deletions
|
@ -13,6 +13,8 @@ services:
|
|||
- MYSQL_DATABASE=petclinic
|
||||
volumes:
|
||||
- "./conf.d:/etc/mysql/conf.d:ro"
|
||||
profiles:
|
||||
- mysql
|
||||
postgres:
|
||||
image: postgres:15.3
|
||||
ports:
|
||||
|
@ -21,3 +23,5 @@ services:
|
|||
- POSTGRES_PASSWORD=petclinic
|
||||
- POSTGRES_USER=petclinic
|
||||
- POSTGRES_DB=petclinic
|
||||
profiles:
|
||||
- postgres
|
||||
|
|
34
pom.xml
34
pom.xml
|
@ -110,7 +110,27 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-docker-compose</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -120,6 +140,18 @@
|
|||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers-bom</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
16
readme.md
16
readme.md
|
@ -66,6 +66,22 @@ docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES
|
|||
Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt)
|
||||
and for [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt).
|
||||
|
||||
Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a profile just like the Spring profile:
|
||||
|
||||
```
|
||||
$ docker-compose --profile mysql up
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
$ docker-compose --profile postgres up
|
||||
```
|
||||
|
||||
## Test Applications
|
||||
|
||||
At development time we recommend you use the test applications set up as `main()` methods in `PetClinicIntegrationTests` (using the default H2 database and also adding Spring Boot devtools), `MySqlTestApplication` and `PostgresIntegrationTests`. These are set up so that you can run the apps in your IDE and get fast feedback, and also run the same classes as integration tests against the respective database. The MySql integration tests use Testcontainers to start the database in a Docker container, and the Postgres tests use Docker Compose to do the same thing.
|
||||
|
||||
## Compiling the CSS
|
||||
|
||||
There is a `petclinic.css` in `src/main/resources/static/resources/css`. It was generated from the `petclinic.scss` source, combined with the [Bootstrap](https://getbootstrap.com/) library. If you make changes to the `scss`, or upgrade Bootstrap, you will need to re-compile the CSS resources using the Maven profile "css", i.e. `./mvnw package -P css`. There is no build profile for Gradle to compile the CSS.
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.samples.petclinic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("mysql")
|
||||
@Testcontainers
|
||||
class MySqlIntegrationTests {
|
||||
|
||||
@ServiceConnection
|
||||
@Container
|
||||
static MySQLContainer<?> container = new MySQLContainer<>("mysql:5.7");
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Autowired
|
||||
private VetRepository vets;
|
||||
|
||||
@Autowired
|
||||
private RestTemplateBuilder builder;
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOwnerDetails() {
|
||||
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
||||
ResponseEntity<String> result = template.exchange(RequestEntity.get("/owners/1").build(), String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.samples.petclinic;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
|
||||
/**
|
||||
* PetClinic Spring Boot Application.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class MysqlTestApplication {
|
||||
|
||||
@ServiceConnection
|
||||
@Profile("mysql")
|
||||
@Bean
|
||||
static MySQLContainer<?> container() {
|
||||
return new MySQLContainer<>("mysql:5.7");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PetClinicApplication.class, "--spring.profiles.active=mysql");
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
|
@ -31,7 +32,7 @@ import org.springframework.samples.petclinic.vet.VetRepository;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
class PetClinicIntegrationTests {
|
||||
public class PetClinicIntegrationTests {
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
@ -55,4 +56,8 @@ class PetClinicIntegrationTests {
|
|||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PetClinicApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.samples.petclinic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", //
|
||||
"spring.docker.compose.profiles.active=postgres" })
|
||||
@ActiveProfiles("postgres")
|
||||
public class PostgresIntegrationTests {
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Autowired
|
||||
private VetRepository vets;
|
||||
|
||||
@Autowired
|
||||
private RestTemplateBuilder builder;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(PetClinicApplication.class) //
|
||||
.profiles("postgres") //
|
||||
.properties( //
|
||||
"spring.docker.compose.profiles.active=postgres" //
|
||||
) //
|
||||
.listeners(new PropertiesLogger()) //
|
||||
.run(args);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOwnerDetails() {
|
||||
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
||||
ResponseEntity<String> result = template.exchange(RequestEntity.get("/owners/1").build(), String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
static class PropertiesLogger implements ApplicationListener<ApplicationPreparedEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(PropertiesLogger.class);
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private boolean isFirstRun = true;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationPreparedEvent event) {
|
||||
if (isFirstRun) {
|
||||
environment = event.getApplicationContext().getEnvironment();
|
||||
printProperties();
|
||||
}
|
||||
isFirstRun = false;
|
||||
}
|
||||
|
||||
public void printProperties() {
|
||||
for (EnumerablePropertySource<?> source : findPropertiesPropertySources()) {
|
||||
log.info("PropertySource: " + source.getName());
|
||||
String[] names = source.getPropertyNames();
|
||||
Arrays.sort(names);
|
||||
for (String name : names) {
|
||||
String resolved = environment.getProperty(name);
|
||||
String value = source.getProperty(name).toString();
|
||||
if (resolved.equals(value)) {
|
||||
log.info(name + "=" + resolved);
|
||||
}
|
||||
else {
|
||||
log.info(name + "=" + value + " OVERRIDDEN to " + resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<EnumerablePropertySource<?>> findPropertiesPropertySources() {
|
||||
List<EnumerablePropertySource<?>> sources = new LinkedList<>();
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
if (source instanceof EnumerablePropertySource enumerable) {
|
||||
sources.add(enumerable);
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue