Skip to main content

Unit Test - Microservices - Business Layer


In this post, I am going to show how to unit test the business layer of the lead microservice that I am developing. A couple of things to keep in mind to write the unit test for the business layer.

  1. These tests run must very fast for quick feedback.
  2. Only the business layer is involved and hence the web layer and repository layer should not be started. In other words, the Spring web tier beans should not be created and neither any database connections should be used. (This helps to achieve #1). 
  3. The repository layer will be mocked using Mockito.
  4. The fluent assert from AssertJ library will be used. 
The source code of the lead business unit test is shown in listing below.

package com.effectiv.crm.business.ut;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import com.effectiv.crm.business.LeadBusinessDelegate;
import com.effectiv.crm.domain.Lead;
import com.effectiv.crm.repository.LeadRepository;
public class LeadBusinessDelegateTest {
private LeadBusinessDelegate leadBusinessDelegate;
private LeadRepository leadRepository;
@Before
public void setUp() {
leadRepository = Mockito.mock(LeadRepository.class);
leadBusinessDelegate = new LeadBusinessDelegate(leadRepository);
}
@Test
public void findOne() {
Lead l = new Lead();
l.setId("1");
l.setFirstName("Virat");
l.setLastName("Kohli");
// mock
when(leadRepository.findOne(any(String.class))).thenReturn(l);
Lead rl = leadBusinessDelegate.findOne("1");
assertThat(rl.getId()).isEqualTo("1");
assertThat(rl.getFirstName()).isEqualTo("Virat");
assertThat(rl.getLastName()).isEqualTo("Kohli");
verify(leadRepository, times(1)).findOne("1");
verifyNoMoreInteractions(leadRepository);
}
@Test
public void findOneDoesNotExist() {
// mock
when(leadRepository.findOne(any(String.class))).thenReturn(null);
Lead rl = leadBusinessDelegate.findOne("1");
assertThat(rl).isNull();
verify(leadRepository, times(1)).findOne("1");
verifyNoMoreInteractions(leadRepository);
}
@Test
public void testFindAll() {
List<Lead> leads = new ArrayList<Lead>();
Lead l = new Lead();
l.setId("1");
leads.add(l);
l = new Lead();
l.setId("2");
leads.add(l);
l = new Lead();
l.setId("3");
leads.add(l);
l = new Lead();
l.setId("4");
leads.add(l);
l = new Lead();
l.setId("5");
leads.add(l);
when(leadRepository.findAll(any(Pageable.class))).thenReturn(new PageImpl<>(leads));
PageRequest p = new PageRequest(1, 5);
Page<Lead> rLeadPage = leadBusinessDelegate.findAll(p);
assertThat(rLeadPage.getTotalElements()).isEqualTo(5L);
assertThat(rLeadPage.getTotalPages()).isEqualTo(1);
List<Lead> rLeads = rLeadPage.getContent();
int counter = 1;
for (Lead ld : rLeads) {
assertThat(ld.getId()).isEqualTo(counter + "");
counter++;
}
verify(leadRepository, times(1)).findAll(p);
verifyNoMoreInteractions(leadRepository);
}
@Test
public void testSave() {
Lead lead = new Lead();
lead.setAnnualRevenue(50000.00D);
lead.setCompany("EFFECTIV");
lead.setDeleted(false);
lead.setDescription("Unit - test - entity :: Lead");
lead.setDesignation("Project Manager");
lead.setEmail("effectvlead@effectiv.com");
lead.setEmailOptOut(false);
lead.setId("1");
// mock
when(leadRepository.save(any(Lead.class))).thenReturn(lead);
Lead rLead = leadBusinessDelegate.save(lead);
assertThat(rLead).isNotNull();
assertThat(rLead.getId()).isEqualTo("1"); //ensure that ID does not change and lead object is returned
verify(leadRepository, times(1)).save(lead);
verifyNoMoreInteractions(leadRepository);
}
@Test
public void testDelete() {
// mock
Lead lead = new Lead();
lead.setAnnualRevenue(50000.00D);
lead.setCompany("EFFECTIV");
lead.setDeleted(false);
lead.setDescription("Unit - test - entity :: Lead");
lead.setDesignation("Project Manager");
lead.setEmail("effectvlead@effectiv.com");
lead.setEmailOptOut(false);
lead.setId("1");
// mock
when(leadRepository.findOne(any(String.class))).thenReturn(lead);
leadBusinessDelegate.delete("1");
ArgumentCaptor<Lead> leadCaptor = ArgumentCaptor.forClass(Lead.class);
verify(leadRepository, times(1)).findOne("1");
verify(leadRepository, times(1)).save(leadCaptor.capture());
verifyNoMoreInteractions(leadRepository);
Lead deletedLead = leadCaptor.getValue();
//check same lead
assertThat(deletedLead.getId()).isEqualTo("1");
//check if deleted flag is true
assertThat(deletedLead.isDeleted()).isEqualTo(true);
}
}
Most of the test methods are straight forward. However, the testDelete the method needs some explanation. Here the findOne method is mocked/stubbed. This ensures that correct lead object is returned by the stub method. The other thing to note is that the verify the method for saving method captures the internal Lead object. Finally, that object is checked to verify that the "id" and "deleted" attributes. This ensures that the correct object was marked for soft delete by changing the "deleted" attribute.

I will pause the microservices test series for couple weeks. In the next post, I will write again about "12-factor app" and then introduce Spring Cloud Config Server and see how we can implement one key factor of cloud-native application. So, stay tuned.

The source code is available at - https://github.com/kdhrubo/playground/tree/develop/lead-backend

Comments

Popular posts from this blog

Part 3 - Integrating Tiles, Thymeleaf and Spring MVC 3

In this post I will demonstrate how to integrate Apache Tiles with Thymeleaf. This is very simple. The first step is to include the tiles and thymeleaf-tiles extension dependencies. I will include them in the pom.xml. Note we wil lbe using Tiles 2.2.2 Listing 1 - parent/pom.xml --- thymeleaf-tiles and tiles dependencies <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Tiles --> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <dependency> <groupId>org.apache.tiles</groupId> <artifactId>tiles-core</artifactId> <version>${tiles.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.tiles</groupId> <artifactId>tiles-template</artifactId> <version>${tiles.version}</version> <scope>compile</s...

Breaking down the CRM monolith

In my previous posts, I have shared some theory regarding microservices. But it's time to start some implementation. I love to write code and see and feel things working. So I will start a series to refactor a monolithic CRM system and transform it into microservices based flexible software. Big ball of mud. Customer Relationship Management(CRM) is that giant software which existed since time immemorial and is used by all companies in some form or shape. Big enterprises will buy CRM software (also known as packages) from top CRM vendors like Oracle, SAP, Salesforce etc and then employ an army of consultants to try and implement it. Most of the classic CRM systems in the market today, even if deployed on the cloud are the big monolithic ball of mud. They are the gigantic piece of software with the huge feature set. Most often those requirements are surplus to the requirement or they will not fit into the processes of the company. So the company has to hire these certified consu...

Getting started with Prime faces 2

Prime faces is an amazing JSF framework from Cagatay Civici ( http://cagataycivici.wordpress.com/ ). Its wonderful because it is easy to use, minimal dependencies, has probably the widest set of controls among all JSF frameworks, easy to integrate with Spring (including Spring Security) , Java EE EJBs, and last but not the least mobile UI support. So I decided to give Prime faces a try, before selecting it to use in my projects. Step 1 – Create Maven 2 project As a first step to integrating Prime faces, create a Maven 2 project in Eclipse. You will need to select ‘maven-archetype-webapp’. Step 2 – Add repositories and dependencies in pom.xml I will be using Prime faces 2 with JSF 2 on Tomcat 6. Since the dependencies for Prime Faces and JSF 2 (JSF 2.0.3 is required) are available on different repositories, I will add them to my pom file first. The listing below shows my pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/X...