In this tutorial, I will help you get started with JUnit 5 – the next generation of JUnit – one of the most popular unit testing frameworks for Java development. I will show you what’s new in JUnit 5 (compared with JUnit 4), and then guide you how to write tests for testing CRUD operations (Create, Retrieve, Update and Delete) for Hibernate framework with MySQL database.

If you’re new to unit testing and TDD (Test Driven Development), I recommend you to follow this JUnit tutorial first.

 

1. What is JUnit 5?

JUnit is not simply an upgrade to JUnit 4, instead it was completely redesigned to take advantages of Java 8 (Lambda expressions, functional interface…), to be modular and more extensible (for better performance and maintenance) and to have better support for IDEs and build tools. JUnit 5 comprises of the following modules:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit platform is the core, foundational module of JUnit 5. JUnit Jupiter module is for writing tests and extensions. And JUnit Vintage module is for running JUnit 3 and JUnit 4 tests.

Although JUnit 5 is a breakthrough change, writing tests with JUnit 5 is still very similar to JUnit 4 so programmers can quickly use JUnit 5 if they were familiar with previous versions of JUnit.

For complete details about JUnit 5 features, I recommend you to look at the official JUnit 5 user guide.

 

2. Differences between JUnit 4 and JUnit 5

JUnit 5 requires Java 8 or higher, whereas JUnit 4 can run with Java 5 or higher. Classes and annotations in JUnit 5 now reside under the new org.junit.jupiter package (so it won’t conflict with JUnit 4 code). Assertions reside in org.junit.jupiter.Assertions class, and assumptions reside in org.junit.jupiter.Assumptions class.

The following table lists the major changes to annotations in JUnit 5, as compared with JUnit 4:

 

For purpose

 

JUnit 5

JUnit 4

 

Declare a test method

 

 

@Test

 

@Test

 

Execute before all test methods

 

@BeforeAll

 

@BeforeClass

 

 

Execute before each test method

 

@BeforeEach

 

@Before

 

 

Execute after each test method

 

@AfterEach

 

@After

 

 

Execute after all test methods

 

@AfterAll

 

@AfterClass

 

 

Skip a test method/class

 

@Disabled

 

@Ignore

 

 

Tagging and filtering tests

 

@Tag

 

@Category

 

 

Run with an extension

 

@RunWith

 

@ExtendWith

 

 

For a complete guide of migration from JUnit 4 to JUnit 5, I recommend you to read the official JUnit 5 user guide.

 

3. JUnit 5 Standard Test class

So, with such changes to annotations, a standard test class in JUnit 5 would look like follows:

import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

public class StandardTests {

	@BeforeAll
	static void initAll() {
		// initializes resources before all test methods
	}
	
	@BeforeEach
	void init() {
		// initializes resources before each test method		
	}
	
	@Test
	void successTest() {
		// this test will pass
	}
	
	@Test
	void failTest() {
		fail("this test will fail");
	}
	
	@Test
	@Disabled
	void skippedTest() {
		// this test is skipped temporarily
	}
	
	@AfterEach
	void tearDown() {
		// releases resources after each test method
	}
	
	@AfterAll
	static void tearDownAll() {
		// releases resources after all test methods
	}	
}

JUnit 5 changes the annotation names for better reflecting their meanings, and the behavior is the same as JUnit 4.

 

4. JUnit 5 Tests for Hibernate CRUD operations

Next, I will share with you some Java code which I wrote to test CRUD operations (Create, Retrieve, Update and Delete) with Hibernate framework, using JUnit 5. You can use the following code examples as a reference.

Maven Dependencies for Hibernate, MySQL and JUnit 5:

Declare the following dependencies in the pom.xml file:

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core</artifactId>
	<version>5.4.4.Final</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.46</version>
</dependency>
<dependency>
	<groupId>org.junit.jupiter</groupId>
	<artifactId>junit-jupiter</artifactId>
	<version>5.6.2</version>
</dependency>

As you can see, the last dependency is for JUnit Jupiter which includes modules for JUnit platform and JUnit Jupiter engine.

 

Code of Entity Class:

Create the Product class with the following code:

package net.codejava;

import javax.persistence.*;

@Entity
@Table(name = "products")
public class Product {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(length = 64, nullable = false, unique = true)
	private String name;
	
	private float price;

	public Product() {
	}	

	public Product(String name, float price) {
		this(null, name, price);
	}

	public Product(Integer id) {
		this.id = id;
	}
	
	public Product(Integer id, String name, float price) {
		this.id = id;
		this.name = name;
		this.price = price;
	}

	// standard getters and setters		
}

This is a simple entity class that maps to products table in the database. If you are new to Hibernate, I recommend you to follow this tutorial: Java Hibernate JPA Annotations Tutorial for Beginners.

 

Code for Hibernate Session Utility

Next, code the HibernateUtil class as follows:

package net.codejava;

import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

public class HibernateUtil {
	public static SessionFactory getSessionFactory() {
		final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
		        .configure() // configures settings from hibernate.cfg.xml
		        .build();
		
		SessionFactory factory = new MetadataSources(registry)
		  		.buildMetadata().buildSessionFactory();
		
		return factory;
	}
}

This utility class simply returns a SessionFactory which is then used to perform CRUD operations. It is useful when using Hibernate in a standalone, simple Java application.

 

Hibernate Configuration File

Next, let’s specify database connection information and some Hibernate properties in the hibernate.cfg.xml file as follows:

<?xml version="1.0" encoding="UTF-8"?>
<hibernate-configuration>      
  <session-factory>
    <property name="connection.url">jdbc:mysql://localhost:3306/testdb</property>
    <property name="connection.username">root</property>
    <property name="connection.password">password</property>
    <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
    <property name="hibernate.hbm2ddl.auto">create-drop</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>
     
    <mapping class="net.codejava.Product" />
    
  </session-factory>
</hibernate-configuration>

In this Hibernate configuration file, I use the property hibernate.hbm2ddl.auto=create-drop so Hibernate will create the database table at runtime, and drop it when finished. So you just need to create the database testdb in MySQL server.

 

Test Class for Hibernate Utility

Next, create the HibernateUtilTest class to test CRUD operations for products, with the initial code as below:

package net.codejava;

import java.util.List;

import org.hibernate.*;
import org.hibernate.query.*;
import org.junit.jupiter.api.*;

public class HibernateUtilTest {

	private static SessionFactory sessionFactory;
	private Session session;
	
	@BeforeAll
	public static void setup() {
		sessionFactory = HibernateUtil.getSessionFactory();
		System.out.println("SessionFactory created");
	}
	
	@AfterAll
	public static void tearDown() {
		if (sessionFactory != null) sessionFactory.close();
		System.out.println("SessionFactory destroyed");
	}
	
	@Test
	public void testCreate() {
	}
	
	@Test
	public void testUpdate() {
	}
	
	@Test
	public void testGet() {	
	}
	
	@Test
	public void testList() {
	}
	
	@Test
	public void testDelete() {
	}	
	
	@BeforeEach
	public void openSession() {
		session = sessionFactory.openSession();
		System.out.println("Session created");
	}
	
	@AfterEach
	public void closeSession() {
		if (session != null) session.close();
		System.out.println("Session closed\n");
	}	
}

As you can see, I use lifecycle methods in JUnit to initialize SessionFactory before all test methods, initialize Session before each test method, and close them accordingly. I think this example explains the use of JUnit’s lifecycle methods perfectly.

Update the test method that tests persisting a product as follows:

@Test public void testCreate() {
	System.out.println("Running testCreate...");
	
	session.beginTransaction();
	
	Product product = new Product("iPhone 10", 699);
	Integer id = (Integer) session.save(product);
	
	session.getTransaction().commit();
	
	Assertions.assertTrue(id > 0);
}

Write code for the testUpdate() method:

@Test public void testUpdate() {
	System.out.println("Running testUpdate...");
	
	Integer id = 1;
	Product product = new Product(id, "iPhone 11", 999);
	
	session.beginTransaction();
	session.update(product);
	session.getTransaction().commit();
	
	Product updatedProduct = session.find(Product.class, id);
	
	assertEquals("iPhone 11", updatedProduct.getName());
}

For testing retrieval operations, we test get a specific product and get all products. Update the testGet() method with below code:

@Test
public void testGet() {
	System.out.println("Running testGet...");
	
	Integer id = 1;
	
	Product product = session.find(Product.class, id);
	
	assertEquals("iPhone 10", product.getName());		
}

Code for the testList() method:

@Test
public void testList() {
	System.out.println("Running testList...");
	
	Query<Product> query = session.createQuery("from Product", Product.class);
	List<Product> resultList = query.getResultList();
	
	Assertions.assertFalse(resultList.isEmpty());
}

And the last test method is for testing delete operation:

@Test
public void testDelete() {
	System.out.println("Running testDelete...");
	
	Integer id = 1;
	Product product = session.find(Product.class, id);
	
	session.beginTransaction();
	session.delete(product);
	session.getTransaction().commit();
	
	Product deletedProduct = session.find(Product.class, id);
	
	Assertions.assertNull(deletedProduct);
}

You can run each test individually. Note that run the testCreate() method first to create the table and first row. Then set hibernate.hbm2ddl.auto=none to keep the data for subsequent tests. You can execute all test methods by using test method order, as described in this guide.

For visual actions, I recommend you to watch the following video:

 

Conclusion

So far you have learned how to get started with JUnit 5 by writing unit tests for CRUD operations with Hibernate framework. I hope you found this tutorial helpful, and you can download the sample project attached below, for reference.

 

Related Tutorials:

 

Other JUnit Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (JUnit5Test.zip)JUnit5Test.zip[JUnit 5 Sample project]15 kB

Add comment

   


Comments 

#1Khoa2021-09-25 10:55
Hi bạn, cảm ơn về bài viết.

Mình có câu hỏi là có cách nào để test mà không bị ảnh hưởng đến DB hay không? Vì bên hàm DAO mình đã close session nên không thể rollback được.

Cảm ơn!
Quote