Core Java

Java Unit Testing Tutorial

This is a tutorial for Java unit testing. In this tutorial, I will create a spring boot application which includes test cases written with Junit, Mockito, and Spring test. The overall unit tests cover 90 percent of the application.

You can also check this tutorial in the following video:

Unit Testing Tutorial – video

1. Introduction

This is a tutorial for Java unit testing. A unit is an individual part which can also become a component of a larger system. In Java world, a unit can be a method or a class. Unit testing is a level of software testing where methods and classes are tested so that every unit of the software works as designed.

Unit testing is the foundation of the “Testing Pyramid” outlined by Mike Cohn. It should be automated and run whenever there is a code change. With that, development is faster with the following benefits:

  • Codes are more reliable when it has a good amount of code coverage.
  • Codes are more reusable because it’s easy to reuse modular code.
  • The cost of fixing a defect detected during unit testing is lesser comparing to when defects detected at higher levels.
  • It increases confidence when changing code. Every change is tested so the unintended impact of changes is identified.

2. Unit Testing Frameworks

There are lots of Java unit testing frameworks. In this example, I will demonstrate unit testing with the following frameworks:

  • Junit – A standard for Java unit testing which provides @Test annotation to specify tests and assertion methods: assertEquals(), assertTrue(), and assertFalse().
  • Mockito – a mocking framework which provides @Mock annotation to mock the dependency with mocking methods: when, thenReturn, doNothing, and doThrow.
  • Spring Test and Spring Boot Test – a test library which tests spring boot application.

I will create a spring boot application which contains test classes to test key methods.

Java Unit Testing Tutorial - Class Digram
Figure 1 Class Digram

3. Technologies Used

The example code in this article was built and run using:

  • Java 8
  • Maven 3.3.9
  • Eclipse Oxygen
  • H2
  • Spring boot (which includes Spring test, JUnit, and Mockito)

4. Maven Project

4.1 Dependencies

Pom.xml includes dependencies for this project.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>jcg.zheng.demo</groupId>
	<artifactId>junit-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-boot-jersey-demo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.14.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

4.2 Application Properties

In this step, I will create an application.properties to configure a H2 database and spring JPA hibernate configuration.

application.properties

spring.datasource.url=jdbc:h2:file:~/main-source;AUTO_SERVER=TRUE
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true

4.3 Application

In this step, I will create an Application.java class which annotates with @SpringBootApplication. It only has a main method to start the application.

Application.java

package jcg.zheng.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

4.4 Person Entity

In this step, I will create a Person class which represents an entity class. It annotates with @javax.persistence.Entity, @javax.persistence.Id, @javax.persistence.GeneratedValue, etc.

Person.java

package jcg.zheng.demo.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Person {

	private String companyName;

	private String fName;
	private String lName;
	private String mName;
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int personId;

	public String getCompanyName() {
		return companyName;
	}

	public String getfName() {
		return fName;
	}

	public String getlName() {
		return lName;
	}

	public String getmName() {
		return mName;
	}

	public int getPersonId() {
		return personId;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	public void setfName(String fName) {
		this.fName = fName;
	}

	public void setlName(String lName) {
		this.lName = lName;
	}

	public void setmName(String mName) {
		this.mName = mName;
	}

	public void setPersonId(int personId) {
		this.personId = personId;
	}

}

4.5 User Not Found Exception

In this step, I will create a UserNotFoundException which extends from RuntimeException.

UserNotFoundException.java

package jcg.zheng.demo.exception;

public class UserNotFoundException extends RuntimeException {

	private static final long serialVersionUID = 3873418545077760440L;

	private final Integer userId;

	public UserNotFoundException(String message, Integer userId) {
		super(message);
		this.userId = userId;
	}

	public Integer getUserId() {
		return userId;
	}
}

4.6 Person Repository

In this step, I will create a PersonRepository interface which extends from JpaRepository and have one customized query – findByCompany.

PersonRepository.java

package jcg.zheng.demo.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import jcg.zheng.demo.entity.Person;

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {

	@Query("SELECT person from Person person WHERE person.companyName = :companyName")
	List<Person> findByCompany(@Param("companyName") String companyName);

}

4.7 User

In this step, I will create a User domain class which has several data members and overwrites the equals and hashCode methods.

User.java

package jcg.zheng.demo.service;

public class User {

	private String companyName;

	private String firstName;

	private String lastName;

	private Integer userId;

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (userId == null) {
			if (other.userId != null)
				return false;
		} else if (!userId.equals(other.userId))
			return false;
		return true;
	}

	public String getCompanyName() {
		return companyName;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public Integer getUserId() {
		return userId;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((userId == null) ? 0 : userId.hashCode());
		return result;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

}

4.8 UserService

In this step, I will create a UserService interface which has four public methods.

UserService.java

package jcg.zheng.demo.service;

import java.util.List;

public interface UserService {

	void deleteById(Integer personId);

	User findById(Integer personId);

	User save(User user);

	List searchByCompanyName(String companyName);

}

4.9 TransformService

In this step, I will create a spring managed component – TransformService which transforms the Person entity to User domain and vice versa.

UserServiceImpl.java

package jcg.zheng.demo.service;

import org.springframework.stereotype.Component;

import jcg.zheng.demo.entity.Person;

@Component
public class TransformService {

	public User toUserDomain(final Person person) {
		User user = new User();
		user.setCompanyName(person.getCompanyName());
		user.setFirstName(person.getfName());
		user.setLastName(person.getlName());
		user.setUserId(person.getPersonId());
		return user;
	}

	public Person toUserEntity(final User user) {
		Person person = new Person();
		person.setCompanyName(user.getCompanyName());
		person.setfName(user.getFirstName());
		person.setlName(user.getLastName());
		if (user.getUserId() != null) {
			person.setPersonId(user.getUserId());
		}
		return person;
	}
}

4.10 UserServiceImpl

In this step, I will create a spring managed component – UserServiceImpl which implements the UserService interface. It depends on the PersonRepository and TransformServie.

UserServiceImpl.java

package jcg.zheng.demo.service;

import java.util.ArrayList;
import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import jcg.zheng.demo.entity.Person;
import jcg.zheng.demo.exception.UserNotFoundException;
import jcg.zheng.demo.repository.PersonRepository;

@Component
@Transactional
public class UserServiceImpl implements UserService {

	@Autowired
	private PersonRepository personDao;

	@Autowired
	private TransformService transformer;

	@Override
	public void deleteById(Integer personId) {
		personDao.delete(personId);
	}

	@Override
	public User findById(Integer personId) {
		Person found = personDao.findOne(personId);

		if (found == null) {
			throw new UserNotFoundException("not found user", personId);
		}
		return transformer.toUserDomain(found);
	}

	@Override
	public User save(User user) {
		Person saved = personDao.save(transformer.toUserEntity(user));
		return transformer.toUserDomain(saved);
	}

	@Override
	public List<User> searchByCompanyName(String companyName) {
		List<Person> persons = personDao.findByCompany(companyName);
		List<User> users = new ArrayList<>();
		for (Person person : persons) {
			users.add(transformer.toUserDomain(person));
		}
		return users;
	}
}

5. JUnit Test

In this section, I will create several test classes that utilize Junit, Mockito, and spring test frameworks. In this example, I will use the following annotations throughout the project.

Framework Common Annotations Comments
JUnit @Test Mark it at a public void method to indicate it is a test case
JUnit @Rule Mark it at a public variable to let JUnit run it as a test rule
JUnit @Before Mark it at a public void method to let Junit execute it before any test case
JUnit @RunWith Specify a test class with a non-default runner
Spring Test @SpringBootTest Set up application context for the testing purpose
Spring Test @DataJpaTest Set up context to test JpaRepository
Mockito @Mock Create a mock object
Mockito @InjectMocks Create an object and inject the marked dependencies

Each test case is structured with Given, When, Then sequences. It sets up the data, invokes the testing method, and verifies the output. This project has 90% code coverage with unit tests.

Figure 2 Code Coverage

5.1 PersonTest

Person is an entity class which annotates with @Entity annotation. It must have a default constructor, so I will test its default constructor in the PersonTest.java

PersonTest.java

package jcg.zheng.demo.entity;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class PersonTest {

	@Test
	public void test_person_default_constructor() {
		Person testClass = new Person();
		
		testClass.setmName("Shan");
		assertEquals("Shan", testClass.getmName());

		testClass.setfName("Mary");
		assertEquals("Mary", testClass.getfName());

		testClass.setlName("Zheng");
		assertEquals("Zheng", testClass.getlName());
	}

}

Execute it with maven command mvn test -Dtest=PersonTest and capture the output here.

Output

Running jcg.zheng.demo.entity.PersonTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec - in jcg.zheng.demo.entity.PersonTest

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

5.2 PersonRepositoryTest

In this step, I will use the @DataJpaTest provided by spring test framework to auto-wire the TestEntityManager and PersonRepository from the spring context. It has a Timeout rule to ensure every test must complete within 2 seconds. It has one setup and four test cases:

  • setup – it verifies that the spring test framework provides the needed beans – entityManger and personDao and uses them to create two persons in database for other tests to use.
  • findByCompany_return_emptylist_when_not_found – it returns an empty list when there is no person matching the given company.
  • findByCompany_return_person_when_found – it finds a list of people which matches the given company.
  • findOne_return_null_when_not_found – it returns a null object when it doesn’t find a user based on the person id.
  • findAll_return_list_when_found – it returns all persons in the database as a list.

PersonRepositoryTest.java

package jcg.zheng.demo.repository;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

import jcg.zheng.demo.entity.Person;

@RunWith(SpringRunner.class)
@DataJpaTest
public class PersonRepositoryTest {

	@Rule
	public Timeout appTimeout = Timeout.millis(2000);

	@Autowired
	private TestEntityManager entityManager;
	@Autowired
	private PersonRepository personDao;

	@Before
	public void setup() {
		assertNotNull(entityManager);
		assertNotNull(personDao);

		// prepare two persons
		Person mary = new Person();
		mary.setfName("Mary");
		mary.setCompanyName("Test");
		entityManager.persist(mary);

		Person alex = new Person();
		alex.setfName("Alex");
		alex.setCompanyName("Alex company");
		entityManager.persist(alex);

	}

	@Test
	public void findAll_return_list_when_found() {
		List<Person> found = personDao.findAll();

		assertNotNull(found);
		assertEquals(2, found.size());
	}

	@Test
	public void findByCompany_return_person_when_found() {
		List<Person> found = personDao.findByCompany("Test");

		assertNotNull(found);
		assertEquals("Mary", found.get(0).getfName());
	}

	@Test
	public void findByCompany_return_emptylist_when_not_found() {
		List<Person> found = personDao.findByCompany("Test-notExist");

		assertNotNull(found);
		assertTrue(found.isEmpty());

	}

	@Test
	public void findOne_return_null_when_not_found() {
		Person found = personDao.findOne(-9);

		assertNull(found);
	}

}

Execute it with maven command mvn test -Dtest=PersonRepositoryTest and capture the output here.

Output

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.913 sec - in jcg.zheng.demo.repository.PersonRepositoryTest
2020-03-14 16:11:17.596  INFO 140944 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1ed6388a: startup date [Sat Mar 14 16:11:08 CDT 2020]; root of context hierarchy
2020-03-14 16:11:17.603  INFO 140944 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

5.3 TransformServiceTest

Although TransformService is managed by spring, but it has no dependency to other services. We will test it with two simple Junit test methods:

  • test_toDomain – it tests the toUserDomain method
  • test_toEntity – it tests the toUserEntity method

TransformServiceTest.java

package jcg.zheng.demo.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;

import jcg.zheng.demo.entity.Person;

public class TransformServiceTest {

	private TransformService testClass = new TransformService() ;

	@Test
	public void test_toDomain() {
		Person person = new Person();
		person.setCompanyName("test company");
		person.setfName("Mary");
		person.setlName("Zheng");
		person.setmName("shan");
		person.setPersonId(1);
		User user = testClass.toUserDomain(person);

		assertNotNull(user);
		assertEquals("test company", user.getCompanyName());
		assertEquals("Mary", user.getFirstName());
		assertEquals("Zheng", user.getLastName());
		assertEquals(1, user.getUserId().intValue());
	}

	@Test
	public void test_toEntity() {
		User user = new User();

		user.setCompanyName("test company");
		user.setFirstName("Mary");
		user.setLastName("Zheng");
		user.setUserId(Integer.valueOf(1));

		Person person = testClass.toUserEntity(user);

		assertNotNull(user);
		assertEquals("test company", person.getCompanyName());
		assertEquals("Mary", person.getfName());
		assertEquals("Zheng", person.getlName());
		assertEquals(1, person.getPersonId());
	}

}

Execute it with maven command mvn test -Dtest=TransformServiceTest and capture the output here.

Output

Running jcg.zheng.demo.service.TransformServiceTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 sec - in jcg.zheng.demo.service.TransformServiceTest

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

5.4 MockUserServiceImplTest

UserServiceImpl class has two dependencies: PersonRepository to access database, TransformService to transform the Person entity to User domain object. In this step, I will use Mockito to mock these two dependencies and how these two dependencies behave when they are invoked inside the UserServiceImpl class.

  • @Mock – mocks the dependency
  • @InjectMocks – mocks the testing class
  • findById_found – tests the findById method when the id is available in database by mocking the personRepository.findOne method to return a person object.
  • findById_not_found – tests the findById method when the id is not found in database by mocking the personRepository.findOne method to return a null object
  • searchByCompanyName_found – tests searchByCompanyName method when a list of people are found in database by mocking personRepository.findByCompany to return a list of person objects
  • searchByCompanyName_not_found – tests searchByCompanyName method when a list of people are not found in database by mocking personRepository.findByCompany to return an empty list.
  • deleteById_is_done_by_dao_delete – tests deleteById by mocking a void method – personRepository .delete
  • mock_db_exception – tests the service will throw exception when database throws exception.

MockUserServiceImplTest.java

package jcg.zheng.demo.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import jcg.zheng.demo.entity.Person;
import jcg.zheng.demo.exception.UserNotFoundException;
import jcg.zheng.demo.repository.PersonRepository;

@RunWith(MockitoJUnitRunner.class)
public class MockUserServiceImplTest {

	private static final String MARY = "Mary";
	private static final String TEST_COMPANY = "Test";
	private Person person = new Person();
	@Mock
	private PersonRepository personDao;

	@InjectMocks
	private UserServiceImpl testClass;

	@Mock
	private TransformService transformer;

	private User user = new User();

	@Test
	public void findById_found() {
		doReturn(person).when(personDao).findOne(Integer.valueOf(1));
		doReturn(user).when(transformer).toUserDomain(person);

		User user = testClass.findById(Integer.valueOf(1));
		assertEquals(MARY, user.getFirstName());
	}

	@Test(expected = UserNotFoundException.class)
	public void findById_not_found() {
		doReturn(null).when(personDao).findOne(Integer.valueOf(1));

		testClass.findById(Integer.valueOf(1));
	}

	@Test
	public void searchByCompanyName_found() {
		List<Person> persons = new ArrayList<>();
		persons.add(person);
		doReturn(persons).when(personDao).findByCompany(TEST_COMPANY);
		doReturn(user).when(transformer).toUserDomain(person);

		List<User> users = testClass.searchByCompanyName(TEST_COMPANY);
		assertEquals(1, users.size());
		assertEquals(MARY, users.get(0).getFirstName());
	}

	@Test
	public void searchByCompanyName_not_found() {
		List<Person> persons = new ArrayList<>();
		doReturn(persons).when(personDao).findByCompany(TEST_COMPANY);
		doReturn(user).when(transformer).toUserDomain(person);

		List<User> users = testClass.searchByCompanyName(TEST_COMPANY);
		assertTrue(users.isEmpty());
	}

	@Test
	public void deleteById_is_done_by_dao_delete() {
		doNothing().when(personDao).delete(Matchers.any(Integer.class));

		testClass.deleteById(Integer.valueOf(1));

		verify(personDao, times(1)).delete(Integer.valueOf(1));
		;
	}

	@Test(expected = Exception.class)
	public void mock_db_exception() {
		doThrow(new Exception("bad db")).when(personDao).delete(Matchers.any(Integer.class));
	}

	@Before
	public void setup() {
		person.setfName(MARY);
		user.setFirstName(MARY);
	}
}

Execute it with maven command mvn test -Dtest=MockUserServiceImplTest and capture the output here.

Output

Running jcg.zheng.demo.service.MockUserServiceImplTest
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.425 sec - in jcg.zheng.demo.service.MockUserServiceImplTest

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

5.5 UserServiceImplTest

Spring test frameworks provides @SpringBootTest which starts the spring context with all the needed objects. In this step, I will create a UserServiceImplTest class which auto-wires UserService with the following testing methods:

  • findById_throw_exception_when_not_found – expects to throw UserNotfoundException when user is not in database.
  • test_save_findById_searchByCompanyName – tests the searchByCompanyName first, then it saves a person in database, then it finds the saved person, finally, it deletes it from database.
  • searchByCompanyName_return_empty_when_not_found – returns an empty list when there is no person matches the company name.

Please note that these tests also test the PersonRepository and TransformService. It takes longer time ( 8.99 seconds) than the unit test – MockUserServiceImpl which takes 0.4 seconds.

UserServiceImplTest.java

package jcg.zheng.demo.service;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import jcg.zheng.demo.exception.UserNotFoundException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {

	@Autowired
	private UserService userService;

	@Test(expected = UserNotFoundException.class)
	public void findById_throw_exception_when_not_found() {
		userService.findById(Integer.valueOf(-1));
	}

	@Test
	public void searchByCompanyName_return_empty_when_not_found() {
		List<User> found = userService.searchByCompanyName("Test");
		assertTrue(found.isEmpty());
	}

	@Test
	public void test_save_findById_searchByCompanyName() {
		User user = new User();
		user.setFirstName("Mary");
		user.setLastName("Zheng");
		user.setCompanyName("Test");
		user = userService.save(user);
		assertNotNull(user.getUserId());

		User foundUser = userService.findById(user.getUserId());
		assertTrue(foundUser.equals(user));
		userService.deleteById(user.getUserId());

		List<User> found = userService.searchByCompanyName("Test");
		assertTrue(found.isEmpty());
	}

}

Execute it with maven command mvn test -Dtest=UserServiceImplTest and capture the output here.

Output

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.995 sec - in jcg.zheng.demo.service.UserServiceImplTest
2020-03-14 16:27:28.967  INFO 140044 --- [       Thread-5] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4a668b6e: startup date [Sat Mar 14 16:27:21 CDT 2020]; root of context hierarchy
2020-03-14 16:27:28.975  INFO 140044 --- [       Thread-5] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

6. Changes

Imaging the application is in use for a while and business requires a change to use a default system user when the given user is not found in the database.

I will update the UserServiceImpl.java class for this change.

UserServiceImplTest.java

package jcg.zheng.demo.service;

import java.util.ArrayList;
import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import jcg.zheng.demo.entity.Person;
import jcg.zheng.demo.exception.UserNotFoundException;
import jcg.zheng.demo.repository.PersonRepository;

@Component
@Transactional
public class UserServiceImpl implements UserService {

 @Autowired
 private PersonRepository personDao;

 @Autowired
 private TransformService transformer;

 @Override
 public void deleteById(Integer personId) {
 personDao.delete(personId);
 }

 @Override
 public User findById(Integer personId) {
 Person found = personDao.findOne(personId);

 if (found == null) {
 Person defaultPerson = new Person();
 defaultPerson.setfName("default");
 defaultPerson.setlName("System");
 defaultPerson.setCompanyName("Default");
 found = defaultPerson;
 }
 return transformer.toUserDomain(found);
 }

 @Override
 public User save(User user) {
 Person saved = personDao.save(transformer.toUserEntity(user));
 return transformer.toUserDomain(saved);
 }

 @Override
 public List<User> searchByCompanyName(String companyName) {
 List<Person> persons = personDao.findByCompany(companyName);
 List<User> users = new ArrayList<>();
 for (Person person : persons) {
 users.add(transformer.toUserDomain(person));
 }
 return users;
 }
}

After this change, I will see the JUnit test failed at findById_not_found immediately.

Failed tests:
  MockUserServiceImplTest.findById_not_found Expected exception: jcg.zheng.demo.exception.UserNotFoundException

Tests run: 6, Failures: 1, Errors: 0, Skipped: 0

In this case, I will fix the test case – findById_not_found to not throw the UserNotFoundException.

	@Test 
	public void findById_not_found() {
		doReturn(null).when(personDao).findOne( Matchers.any(Integer.class));
		 
		doReturn(user).when(transformer).toUserDomain(Matchers.any(Person.class));
		
		User default_user = testClass.findById(Integer.valueOf(1));
		assertNotNull(default_user);
		 
	}

7. Unit Testing Tutorial – Summary

In this example, I demonstrated how to write a unit test in Junit, Mockito, and Spring test frameworks. As we have seen here, the Mockito test is faster than the spring Jpa test. Spring test is not truly unit testing as it relies on the spring context.

There are other common testing frameworks which support unit testing:

  • TestNG – similar to Junit and enables customized tests which reads test data from a spreadsheet.
  • EasyMock – similar to Mockito.
  • PowerMock – can mock static methods, constructors, and private methods.

8. Download the Source Code

Download
You can download the full source code of this example here: Java Unit Testing Tutorial

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button