1. Testcontainers is introduced:

Testcontainers is a Java library that supports JUnit testing, provides a common database, SeleniumWeb browser, or any lightweight, one-off instance that can run inside a Docker container.

The test container makes it easier to do the following types of tests:

Data access Layer integration test:

Test your data access layer code with containerized instances of MySQL, PostgreSQL, or Oracle databases, but without complex setup on the developer’s computer, and tests will always start with known database state, avoiding “junk” data. You can also use any other database type that can be containerized.

Application integration tests:

Use to run applications in short-term test mode with dependencies (such as databases, message queues, or Web servers).

UI/Acceptance testing:

Automate UI testing using a Selenium compatible containerized Web browser. Each test can get a new instance of the browser without worrying about browser state, plug-in versions, or automatic browser upgrades. You will get a video record of each test session or test failure.

More:

You can check out various contributed modules or create your own custom container classes using GenericContainer as a base.


2.Testcontainers practice examples:

Testcontainers provides a variety of off-the-shelf application containers associated with testing, as shown below:

In this article, you’ll demonstrate tests that integrate the PostgresQL container with the MockServer container.

Testcontainers requirements:

1.Docker

2. Supported JVM testing frameworks: JUnit4, JUnit5, Spock…

2.1 Integrate postgresQL tests

Rely on:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.12.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
  	<! Mysql, mariadb, and so on -->
    <artifactId>postgresql</artifactId>
    <version>1.12.5</version>
    <scope>test</scope>
</dependency>
Copy the code

Configuration:

In the project of the SRC/test/resources/application. The postgresql yml file configuration information

# set the driver to org. Testcontainers. JDBC. ContainerDatabaseDriver, it is an agent testcontainers JDBC driver. When initializing the data source, this driver is responsible for starting the required Docker container. Spring. The datasource. The driver - class - name = org. Testcontainers. JDBC. ContainerDatabaseDriver # sets the JDBC URL to JDBC: tc: < database Image > : <version> : /// so that Testcontainers knows which database to use. # TC_INITSCRIPT = specified database initialization script file location spring. The datasource. Url = JDBC: tc: postgresql: 9.6: / / /? TC_INITSCRIPT = file: SRC/main/resources/init_db SQL # set dialect clear to realize database dialect, or in the start application receives an exception. When you use in your application when the JPA (through the Spring Data JPA), this step is necessary Spring. JPA. Database - platform = org. Hibernate. The dialect. PostgreSQL9DialectCopy the code

Test examples:

In order to use TCS in @DatajPatest, you need to make sure that you use the application-defined (auto-configured) data source. You can use @ AutoConfigureTestDatabase annotation tests to do this easily, as shown below:

@RunWith(SpringJUnit4ClassRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class OwnerRepositoryTests {

    @Autowired
    private OwnerRepository ownerRepository;

    @Test
    void findAllReturnsJohnDoe(a) { // as defined in tc-initscript.sql
        var owners = ownerRepository.findAll();
        assertThat(owners.size()).isOne();
        assertThat(owners.get(0).getFirstName()).isEqualTo("John");
        assertThat(owners.get(0).getLastName()).isEqualTo("Doe"); }}Copy the code

The above tests will be performed using PostgresQL containers provided by Testcontainers to eliminate external environment interference.

When we need to do integration testing with a local database, we can simply replace the above two annotations with @springboottest:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class OwnerResourceTests {

    @Autowired
    WebApplicationContext wac;

    @Test
    void findAllReturnsJohnDoe(a) throws Exception {
        given()
               .webAppContextSetup(wac)
        .when()
                .get("/owners")
        .then()
                .status(HttpStatus.OK)
                .body(
                        "_embedded.owners.firstName", containsInAnyOrder("John"),
                        "_embedded.owners.lastName", containsInAnyOrder("Doe")); }}Copy the code

The above tests will be performed using the database of the real run environment.


2.2 Integrate mockServer tests

Mock Server can be used to simulate HTTP services by matching requests to user-defined expectations.

Rely on:

				<dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>mockserver</artifactId>
            <version>1.12.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mock-server</groupId>
            <artifactId>mockserver-netty</artifactId>
            <version>5.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.mock-server</groupId>
            <artifactId>mockserver-client-java</artifactId>
            <version>5.5.4</version>
        </dependency>
Copy the code

Test examples:

// Create a MockServer container
@Rule
public MockServerContainer mockServer = new MockServerContainer();
Copy the code

And using Java MockServerClient to set simple expectations.

new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
                .when(request()
                        .withPath("/person")
                        .withQueryStringParameter("name"."peter"))
                .respond(response()
                        .withBody("Peter the person!"));
/ /... When a get request is made to '/person? Name = Peter 'returns "Peter the person!"
Copy the code

Testing (testing with RestAssured) :

RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
RestAssured.port = mockServer.getServerPort();
given().queryParam("name"."peter")
                .get("/person")
                .then()
                .statusCode(HttpStatus.OK.value())
                .body(is("Peter the person!"));
Copy the code

The complete code is as follows:

@RunWith(SpringJUnit4ClassRunner.class)
public class OneTests {
    @Rule
    public MockServerContainer mockServer = new MockServerContainer();

    @Test
    public void v(a) {
        RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
        RestAssured.port = mockServer.getServerPort();

        new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
                .when(request()
                        .withPath("/person")
                        .withQueryStringParameter("name"."peter"))
                .respond(response()
                        .withBody("Peter the person!"));

        given().queryParam("name"."peter")
                .get("/person")
                .then()
                .statusCode(HttpStatus.OK.value())
                .body(is("Peter the person!")); }}Copy the code

3. Conclusion:

Testcontainers easily solves the problems of integration tests where the test code is coupled to the local component, resulting in various unexpected failures (for example, integration tests are affected by dirty data in the local database, and the test results fail unexpectedly when multiple integration tests are running at the same time due to mutual interference). The author prepared a set of database specially for integration test to isolate data from other environments, but the problem of interference between multiple integration tests was still encountered, and Testcontainers easily solved the author’s problem.


🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟 🌟

Welcome to my blog:blog.dongxishaonian.tech

Pay attention to the author’s public account, push all kinds of original/high quality technical articles ⬇️