JUnit 5 Tutorial for Beginner - Test CRUD for Hibernate
- Details
- Written by Nam Ha Minh
- Last Updated on 24 June 2020   |   Print Email
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:
- JUnit Tutorial for beginner with Eclipse
- JUnit 5 - How to Run Test Methods in Order
- Java Hibernate JPA Annotations Tutorial for Beginners
Other JUnit Tutorials:
- JUnit Tutorial for beginner with Eclipse
- How to compile and run JUnit tests in command line
- JUnit Test Suite Example - How to create and run test suite in command line and Eclipse
- JUnit Test Exception Examples - How to assert an exception is thrown
Comments
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!