DAO integration test
Use this demo code as a guideline
The project is setup in accordance with the JPA setup guidelines
You are welcome to follow this guided video tutorial - which contains a run-through of what comes below:
The integration tests relies on a number of dependencies:
- JUnit 6 (org.junit.jupiter)
- Hamcrest (org.hamcrest)
- Testcontainers (org.testcontainers)
- Logging (ch.qos.logback and org.slf4j)
If you followed the JPA setup tutorial, you should already have these dependencies in place. If not, make sure to add them to your pom.xml file before proceeding with the test setup.
- Create a new test class in the
test/java/app/daosfolder. Use IntelliJ’s test generation feature to create a new test class for your DAO. For example, if you have aStudyDAOclass, you can create aStudyDAOTestclass. Use JUnit 6 as the testing framework.
This class will be responsible for providing an EntityManagerFactory that is configured to use Testcontainers. This allows us to run our tests against a real PostgreSQL database that is spun up in a Docker container.
In the /test/java/app/ folder, create a new package called config and inside it create a new class called HibernateTestConfig.class with the following content:
package app.config;
import jakarta.persistence.EntityManagerFactory;
import java.util.Properties;
public final class HibernateTestConfig {
private static volatile EntityManagerFactory emf;
private HibernateTestConfig() {}
public static EntityManagerFactory getEntityManagerFactory() {
if (emf == null) {
synchronized (HibernateTestConfig.class) {
if (emf == null) {
emf = HibernateEmfBuilder.build(buildProps());
}
}
}
return emf;
}
private static Properties buildProps() {
Properties props = HibernateBaseProperties.createBase();
// Testcontainers JDBC driver
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.hbm2ddl.auto", "create-drop");
return props;
}
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StudyDAOTest {
private final EntityManagerFactory emf = HibernateTestConfig.getEntityManagerFactory();
private StudyDAO studyDAO;
private Map<String, Study> seeded;
@BeforeEach
void beforeEach(){
seeded = StudyTestPopulator.populate(emf);
studyDAO = new StudyDAO(emf);
}
@AfterAll
void shutdown() {
emf.close();
}
Note the use of @TestInstance(TestInstance.Lifecycle.PER_CLASS). This allows us to use non-static methods annotated with @BeforeAll and @AfterAll, which is useful for managing the lifecycle of the EntityManagerFactory. In this setup, we initialize the EntityManagerFactory once for all tests and close it after all tests have run. The @BeforeEach method is used to populate the database with known data before each test, ensuring that each test runs in a consistent environment.
Also, note that we are using a StudyTestPopulator class to populate the database with test data. This is a common pattern to ensure that your tests have a known state to work with. You can create this class in the test/java/app/testutils package and implement it to insert test data into the database before each test runs.
Look how it done here: StudyTestPopulator. We are using a map to store the persisted entities, so we can easily access them in the tests later on to check that the right data is being returned from the DAO methods. This is a common pattern to ensure that your tests have a known state to work with. Also that we get a handle on the autogenerated primary keys, which is often necessary when writing tests for DAO methods that rely on the primary keys of the entities.
Craft your tests of each DAO method, one by one. Make sure that you cover most cases:
First test that the number of rows in the tables match
Secondly, make sure that the entities are the right ones, and that the attributes have the expected content. You might need to create
equalsandhashcodemethods on the entities to help the junit / hamcrest matchers to work correctly. Hamcrest offers the matchersamePropertyValuesAsthat can help you getting the job done like this:assertThat(p1ToUpdate, samePropertyValuesAs(p1));