Used to write Integration or Repository tests.
Create ephemeral external dependencies like databases, MQs, etc.. for test purposes. Pulls Docker images and runs containers for test scope & duration, needs Docker pre-installed on the system to work.
@SpringBootTest
@Testcontainers
public class MyTest{
// Postgres specific code
@Container
static PostgreSQLContainer postgreSQLContainer =
new PostgreSQLContainer(DockerImageName.parse("postgres:14-alpine"));
// alternatively, for any Docker image (recommended)
@Container
static GenericContainer sqlContainer =
new GenericContainer(DockerImageName.parse("postgres:14-alpine"));
// db properties
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry){
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
}
}
JDBC URL Short Form: Just define a database JDBC URL like below and all container related stuff will be taken care of, no need for testcontainer related annotations too!
@SpringBootTest
@TestPropertySource(properties = {
"spring.datasource.url=jdbc:tc:postgresql:14-alpine/foobar_database"
})
public class MyTest{
// tests
}
We can also externalize common container configs to another class too.
How to populate container DB with data:
.sql
scripts to create schema, populate data, etc…) [clarification, tutorial].sql
file containing queries to populate container DB, this file is run automatically by Testcontainers dependency [link]https://www.testcontainers.org
Writing tests for reactive services.
Maven dependency:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope> ย ย ย ย
<version>3.2.3.RELEASE</version>
</dependency>
Flux<String> source = Flux.just("Apple", "Ball", "Cat", "Dog");
StepVerifier
.create(source)
.expectNext("Apple")
.expectNextMatches(name -> name.startsWith("B"))
.expectNext("Cat", "Dog")
.expectComplete()
.verify();
// create a source on the go with TestPublisher
final TestPublisher<String> testPublisher = TestPublisher
.<String>create()
.next("First", "Second", "Third")
.complete();
// verify using StepVerifier
StepVerifier
.create(testPublisher.flux())
.expectNext("First")
.expectNext("Second", "Third")
.expectComplete()
.verify();
https://www.baeldung.com/reactive-streams-step-verifier-test-publisher
A small library to test async services; the status is obtained by aย Callableย that polls our service at defined intervals (default 100ms) after a specified initial delay (default 100ms), it will poll for a max duration (default 10s) post which it will fail the test throwing ConditionTimeoutException
@Test public void updatesCustomerStatus() {
// Publish an asynchronous message to a broker (e.g. RabbitMQ):
messageBroker.publishMessage(updateCustomerStatusMessage);
// Awaitility lets you wait until the asynchronous operation completes:
await().atMost(5, SECONDS).until(customerStatusIsUpdated());
https://www.baeldung.com/awaitility-testing
Create and up a mock server that mimics an external API integration, instead of calling the real one (that may cost us money).
// create mock server
@RegisterExtension
static WireMockExtension wireMockServer = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();
// set property to point to mock server instead of external API (GitHub in this case)
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("github.api.base-url", wireMockServer::baseUrl);
}
@Test
void shouldGetFailureResponseWhenGitHubApiFailed() throws Exception {
String username = "abhishekarya1";
// stub API call (path pattern matching)
wireMockServer.stubFor(WireMock.get(urlMatching("/users/.*"))
.willReturn(aResponse().withStatus(500)));
// perform actual call using MockMvc
String expectedError = "Fail to fetch github profile for " + username;
this.mockMvc.perform(get("/api/users/{username}", username))
.andExpect(status().is5xxServerError())
.andExpect(jsonPath("$.message", is(expectedError)));
}
MockServer is another library that is very similar to WireMock, has a slightly simpler syntax and can be loaded as a Testcontainer too.
Created by Microsoft for writing end-to-end tests; alternative to Selenium.
Calls our service as a browser agent - Chromium, Firefox, etc… loads the page and we can then specify which buttons user clicks, doing search from search bar, etc…
https://playwright.dev/java/docs/running-tests
Performance testing; written in Go, extremely fast but allows only HTTP requests