Skip to main content
Dat 3rd Sem Fall 2025
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

DAO integration test

With test containers

Use this demo code as a guideline for how to setup DAO integration tests with test containers. Notice that the integration test is in the “final-with-test” branch of the repository. If you wish to follow this tutorial, then clone the main branch and go from there.

The project is setup in accordance with the JPA setup guidelines and the example is based on this JPA CodeLab exercise.

Video tutorial

You are welcome to follow this guided video tutorial - which contains a run-through of what comes below:

1. Update your pom.xml file

The integration tests relies on a number of dependencies:

  • JUnit 5 (org.junit.jupiter)
  • Hamcrest (org.hamcrest)
  • Testcontainers (org.testcontainers)
  • Logging (ch.qos.logback and org.slf4j)

Copy the dependencies from this version of the pom.xml. Remember to also get the version properties:

<testcontainers.version>1.20.4</testcontainers.version>
<logback-classic>1.5.16</logback-classic>
<org.slf4j>2.0.16</org.slf4j>
<hamcrest>3.0</hamcrest>

Newer versions might be available at Maven Central. Check them out, but make sure the versions you pick are stable.

2. Setup a StudentDAOTest class skeleton

The easiest way to do this in IntelliJ is to choose “Generate…” in the DAO class - and then choose “Test…”.

Testwizard

3. Add class variables

  • EntityManagerFactory
  • DolphinDAO
  • Testdata (p1, p2, p3)

Example:

private EntityManagerFactory emf;
private DolphinDAO dolphinDAO;
private Person p1, p2, p3;
private List<Person> persons;

4. Setup @BeforeAll

    @BeforeAll
    void initOnce() {
        emf = HibernateConfig.getEntityManagerFactoryForTest();
        dolphinDAO = new DolphinDAO(emf);
    }

5. Setup @BeforeEach

  • This method is executed before each individual unit test.
  • So delete all tables
  • Reset autogenerated primary keys
  • Populate the tables with known data

This is an example:

@BeforeEach
    void setUp() {
        try (EntityManager em = emf.createEntityManager()) {
            em.getTransaction().begin();
            em.createNativeQuery("TRUNCATE TABLE fee, person_detail, person RESTART IDENTITY CASCADE")
                    .executeUpdate();
            em.getTransaction().commit();
        }
        catch (Exception e) {
            throw new RuntimeException("Failed to truncate tables", e);
        }

        persons = PersonPopulator.populatePersons(dolphinDAO);
        if (persons.size() == 3) {
            p1 = persons.get(0);
            p2 = persons.get(1);
            p3 = persons.get(2);
        } else {
            throw new ApiException(500, "Populator doesnt work");
        }
    }

6. Setup a populator class

You can find an example of a Populator class here. This trick here is to be able to get a handle on the entities you persist. One way is to store the entities after persisting them into an arraylist, and then return the array to the test class. By having references to the entities in the test database, you can easily pick out the ids later.

In this example, we will declare some variables in the test class:

private Person p1, p2, p3;
private List<Person> persons;

In the @BeforeEach section, we will call a populator method that return an array of entities and then assign the s1 and s2 to the persisted test entities:

persons = PersonPopulator.populatePersons(dolphinDAO);
p1 = persons.get(0);
p2 = persons.get(1);
p3 = persons.get(2);

7. Fix up the HibernateConfig file

Make sure the HibernateConfig file is updated with right test properties:

private static Properties setTestProperties(Properties props) {
   props.put("hibernate.connection.driver_class", "org.testcontainers.jdbc.ContainerDatabaseDriver");
   props.put("hibernate.connection.url", "jdbc:tc:postgresql:16.2:///test_db");
   props.put("hibernate.archive.autodetection", "hbm,class");
   props.put("hibernate.show_sql", "false");
   props.put("hibernate.hbm2ddl.auto", "create-drop");
   return props;
}

8. Add a logger to limit the log output

  • You don’t need this step, but spinning up the test container generates a lot of verbose log info by default. If you configure a logger, you can limit the noise:

  • Create a resource folder in the test folder. This is how the folders can be structured: Folders

  • Add a file called “logback-test.xml” with the content:

    <configuration>
       <!-- Suppress debug logs from Testcontainers -->
       <logger name="org.testcontainers" level="INFO"/>
       <logger name="org.testcontainers.utility" level="WARN"/>
       <logger name="docker" level="WARN"/>
    
       <!-- Optional: Reduce Hibernate verbosity -->
       <logger name="org.hibernate.SQL" level="WARN"/>
       <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="WARN"/>
       <logger name="org.hibernate.engine.jdbc.batch.internal.BatchingBatch" level="ERROR"/>
    
       <!-- Console Appender -->
       <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
          <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg%n</pattern>
          </encoder>
       </appender>
    
       <root level="INFO">
          <appender-ref ref="STDOUT"/>
       </root>
    </configuration>
    

9. Create your tests one by one

Craft your tests of each DAO method, one by one. Make sure that you cover most cases:

  1. First test that the number of rows in the tables match

  2. Secondly, make sure that the entities are the right ones, and that the attributes have the expected content. You might need to create equals and hashcode methods on the entities to help the junit / hamcrest matchers to work correctly. Hamcrest offers the matcher samePropertyValuesAs that can help you getting the job done like this:

    assertThat(p1ToUpdate, samePropertyValuesAs(p1));