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:
Table Of Contents
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()
, andassertFalse()
. - Mockito – a mocking framework which provides
@Mock
annotation to mock the dependency with mocking methods:when
,thenReturn
,doNothing
, anddoThrow
. - 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.
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.
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
andpersonDao
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 thetoUserDomain
methodtest_toEntity
– it tests thetoUserEntity
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 classfindById_found
– tests thefindById
method when the id is available in database by mocking thepersonRepository.findOne
method to return a person object.findById_not_found
– tests thefindById
method when the id is not found in database by mocking thepersonRepository.findOne
method to return anull
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
– testssearchByCompanyName
method when a list of people are not found in database by mockingpersonRepository.
findByCompany to return an empty list.deleteById_is_done_by_dao_delete
– testsdeleteById
by mocking a void method –personRepository
.deletemock_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 throwUserNotfoundException
when user is not in database.test_save_findById_searchByCompanyName
– tests thesearchByCompanyName
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
You can download the full source code of this example here: Java Unit Testing Tutorial