Share database connection between JUnit 5 test classes - spring

I have 6 JUnit classes into different test packages. I want to use application.properties file which is used by Spring framework to setup database connection.
The problem is how to create database connection when first JUnit test is run and reuse it for all Classes. At the end to close the connection properly.

You can use Testcontainers for this. If you want to reuse an existing database container (e.g. PostgreSQL or MySQL), make sure to use a manual container lifecycle approach and start the container once (e.g. inside an abstract test class). Testcontainers calls this Singleton containers:
abstract class AbstractContainerBaseTest {
static final MySQLContainer MY_SQL_CONTAINER;
static {
MY_SQL_CONTAINER = new MySQLContainer();
MY_SQL_CONTAINER.start();
}
}
class FirstTest extends AbstractContainerBaseTest {
#Test
void someTestMethod() {
String url = MY_SQL_CONTAINER.getJdbcUrl();
// create a connection and run test as normal
}
}
Once your JVM terminates, your container will be deleted.
Another approach would be the reuse feature of Testcontainers:
static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer()
.withDatabaseName("test")
.withUsername("duke")
.withPassword("s3cret")
.withReuse(true);
If your database setup is similar for all your tests and you opt-in for this feature, Testcontainers will reuse an already started container. Keep in mind that with this approach you have to clean up the container for yourself as they stay alive after all tests finished.

Related

Start a Testcontainers container during each fresh application context (#DirtiesContext), with injection of properties (#DynamicPropertySource)

I am using Testcontainers to execute integration tests in a Spring project. I am using JUnit 4.x and Cucumber 7.4.1.
I want to start a docker-compose.yml-based set of containers before each test, as that makes it easy to start from scratch. Hence, I am using #DirtiesContext.
The challenge here is that certain application properties, such as spring.rabbitmq.host, are needed before the actual application context can start. So I need to inject them beforehand. There is #DynamicPropertySource for that. But then I also need to get access to my context-scoped docker containers. The best I came up with so far is the following:
#CucumberContextConfiguration
#SpringBootTest(classes = TestConfig.class)
#DirtiesContext
public class CucumberITConfig {
#DynamicPropertySource
private static void properties(DynamicPropertyRegistry registry) {
DockerComposeContainer container = new DockerComposeContainer(new File("docker-compose.yml"))
.withExposedService("rabbitmq", 5672);
container.start();
registry.add("spring.rabbitmq.host", () -> container.getServiceHost("rabbitmq", 5672));
}
}
This constructs new docker containers locally and waits for the host to be passed to the registry. While this seem to work, this looks more like a hackish approach to me. Also, a problem here is that the containers stack up after each test. That is, in the 7th test, for example, the containers from all previous 6 cycles are still running.
Are there better approaches to start Testcontainers-based docker containers before each application context, while also being able to destruct them afterwards?
If you are using #DirtiesContext after all, you can set these values as System properties and omit #DynamicPropertySource altogether. However, as others have pointed out, solving test pollution by re-creating the Spring context and all dependent services for every test class will be very slow and is generally considered an anti-pattern.

Cassandra unit spring - Launch an embedded Cassandra only once per test class

I am setting up integration tests for my spring-boot application using cassandra-unit-spring maven dependency. I am able to run my tests which invoke the spring-boot application which in turn accesses an in-memory embedded Cassandra database.
Below is the code for my test class
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({CassandraUnitDependencyInjectionTestExecutionListener.class, DependencyInjectionTestExecutionListener.class})
#CassandraDataSet(value = "cassandra/dbcreate.cql", keyspace = "test")
#EmbeddedCassandra
#SpringBootTest({"spring.data.cassandra.port=9142"})
public class IntegrationTest {
#Autowired
private TestRepository testRepository;
#Test
public void testFindById() {
Token token = generateRandomToken();
testRepository.insert(token);
Optional<Token> tokenStored = testRepository.findById(token.getKey());
compareReplayToken(token, tokenStored.get()); //This method does the assertions
}
}
This single test invokes the embedded Cassandra and creates the keyspace and tables from the commands in the cassandra/dbcreate.cql file. After the test runs, the keyspace and tables are dropped.
Till now, it is fine. But, if I try to add multiple tests in this class, this approach creates the keyspace and tables at the beginning of each test and then drops them once the test runs.
And the dbcreate.cql file has a lot of commands to create multiple tables and when these commands run for each test, this makes my tests really slow.
Also, this problem multiplies when I try to have multiple such test classes.
Possible solution that I could think of is:
Have a separate cql file for each test class that has limited cql commands concerned with that class only - Again, this doesn't solve the problem of the database reset for each test in a single class
I want to run all my integration tests for a single launch of this embedded Cassandra and the tables and keyspace should be created and dropped only once for a fast execution
What should be the ideal solution for such a problem?
Any help is much appreciated.
Thanks!

How to setup local dev environment properties of Spring Boot based on docker compose with testcontainers?

I'm developing a service that has many dependencies like Redis, PubSub, S3, ServiceC and ServiceD. When I hit an endpoint in development, e.g. /data/4, a http request to ServiceC is performed.
So to that to work, something that mocks ServiceC must run. In my case it's wiremock (since I cant run ServiceC directly, as it also has many dependencies). All of these MockServices are in a docker-compose-dev file.
Now to my question: How can I run the docker-compose with testcontainers, get the assigned ports, and set the correct properties to the WebClient has the right mock url + port?
What lifecycle hook can I use to run before spring boot starts, and also can configure the properties?
One downside would be an increased boot time in the dev mode though, but I can't assign fixed ports in docker compose file, because they might be used on the developer's machine. So makes no sense to ship url defaults for the service urls on localhost.
Testcontainers supports starting Docker Compose out-of-the-box using the DockerComposeContainer class. While creating an instance of this class you have to expose all your containers:
#Testcontainers
public class DockerComposeTest {
#Container
public static DockerComposeContainer<?> environment =
new DockerComposeContainer<>(new File("docker-compose.yml"))
.withExposedService("database_1", 5432, Wait.forListeningPort())
.withExposedService("keycloak_1", 8080,
Wait.forHttp("/auth").forStatusCode(200)
.withStartupTimeout(Duration.ofSeconds(30)));
#Test
void dockerComposeTest() {
System.out.println(environment.getServicePort("database_1", 5432));
System.out.println(environment.getServicePort("keycloak_1", 8080));
}
}
The example above maps a PostgreSQL database and Keycloak to a random ephemeral port on your machine. Right after the wait checks pass, your test will run and you can access the actual port with .getServicePort().
A possible solution for the WebClient configuration is to use a ApplicationContextInitializer as you somehow have to define the Spring Boot property before the container starts:
public class PropertyInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// static access to environment variable of the test
TestPropertyValues
.of("your_web_client_base_url=http://localhost:" + environment.getServicePort("serviceC", 8080) + "/api")
.applyTo(configurableApplicationContext);
}
}
You can then register the initializer for your integration tests with:
#Testcontainers
#ContextConfiguration(initializers = {PropertyInitializer.class})
class DockerComposeTest {
}
As an alternative, you could also try to solely mock all HTTP communication to external services with WireMock.

SpringBoot, test containers to test queries sent to external DB

My Spring app has its own DB for persistence.
The same app needs to send ad-hoc queries to external databases.
Queries are provided by users.
App takes SQL query provided by user
App takes external database type (postgres / oracle / whatever jdbc)
App submits adhoc query in runtime to external DB
App returns result as json to user
Is there any way to utilize spring test containers in order to test this functionaly?
My goal is:
Write tests for every supported DB
each test starts test container with supported DB (some subset of these I suppose: https://www.testcontainers.org/modules/databases/)
each test uploads sample data to container DB
each test runs set of "must work" queries against it.
I see many examples where App itself is tested against test containers, but can I just start container w/o plugging it as App persistence DB?
can I just start container w/o plugging it as App persistence DB?
Yes, this is perfectly possible.
Testcontainers in itself has nothing to do with Spring or Spring Boot.
What you would do is:
pick the container you want to use (different container for different databases
instantiate the container
start it up
construct a DataSource from it
Use that DataSource for your tests.
Spring Data JDBC does exactly that to run tests against various databases.
I add the class doing that for MySQL in the end.
It is a Spring application context configuration, but you could put that in a JUnit before method, a JUnit 4 rule or a JUnit 5 extension or just a normal method that you call at the start of your test.
#Configuration
#Profile("mysql")
class MySqlDataSourceConfiguration extends DataSourceConfiguration {
private static final MySQLContainer MYSQL_CONTAINER = new MySQLContainer().withConfigurationOverride("");
static {
MYSQL_CONTAINER.start();
}
/*
* (non-Javadoc)
* #see org.springframework.data.jdbc.testing.DataSourceConfiguration#createDataSource()
*/
#Override
protected DataSource createDataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(MYSQL_CONTAINER.getJdbcUrl());
dataSource.setUser(MYSQL_CONTAINER.getUsername());
dataSource.setPassword(MYSQL_CONTAINER.getPassword());
dataSource.setDatabaseName(MYSQL_CONTAINER.getDatabaseName());
return dataSource;
}
#PostConstruct
public void initDatabase() throws SQLException, ScriptException {
ScriptUtils.executeSqlScript(createDataSource().getConnection(), null, "DROP DATABASE test;CREATE DATABASE test;");
}
}

CI/CD Implementation in WebSphere Liberty 19.0.0.8 server with Maven

Am developing application using Maven it has EJB layer. I configured datasource in WebSphere Liberty server. All transaction can be handle by the server. Am using Jenkins to build a application. I would like to create CI/CD implementation. For that I tried add Junit test in application. but am unable to connect database while doing Jenkins build. Because there is no communication b/w server and Jenkins while doing build. How can I create Junit that handle database connection and EJB without Mock.?
One possible solution you could use here is an integration testing library called MicroShed Testing. It is created for integration tests with JavaEE/MicroProfile app servers such as Liberty, and can be used to test your application with external resources running such as DBs.
MicroShed Testing is ideal if you are running your Liberty application inside of a Docker container. If you are running in Docker, you can easily write an integration test that looks something like this:
#MicroShedTest
#SharedContainerConfig(AppContainerConfig.class)
public class DatabaseTest {
#Inject
public static MyJAXRSEndpoint personSvc;
#Test
public void testGetPerson() {
Long bobId = personSvc.createPerson("Bob", 24);
Person bob = personSvc.getPerson(bobId);
assertEquals("Bob", bob.name);
assertEquals(24, bob.age);
}
}
To get this working, you can define your application topology in the class referenced by the #SharedContainerConfig annotation like this:
public class AppContainerConfig implements SharedContainerConfiguration {
#Container
public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>()
.withNetworkAliases("testpostgres")
.withDatabaseName("testdb");
#Container
public static MicroProfileApplication app = new MicroProfileApplication()
.withEnv("POSTGRES_HOSTNAME", "testpostgres")
.withEnv("POSTGRES_PORT", "5432")
.withAppContextRoot("/myservice");
#Override
public void startContainers() {
postgres.start();
app.start();
}
}
The above code will do the following steps:
1) Build your application into a Docker container, using the Dockerfile in your repo
2) Start up the docker container for your app AND the postgresql database (or any other DB container you may need)
3) Wait for containers to be ready, then run tests that invoke HTTP requests on the running containers
I find this approach nice because it works the same way anywhere Docker is installed -- either locally on your machine or in your CI/CD pipeline.
For more info on MicroShed Testing, I recommend checking out the website here:
https://microshed.org/microshed-testing/
and especially the examples, which include a Liberty+Database example:
https://microshed.org/microshed-testing/features/99_Examples.html
Disclaimer: I work on Liberty and MicroShed Testing

Resources