Unitest by using mockito(one class is dependent with other scenario) - tdd

Hi I am very new to mocking framework. Can any one please help me to write a junit by using any mocking framework. Below is my requirement
I wanted to write a unitest for getListOfEmp method by using mock with expected value.
Note Here I am facing difficulty to mock EmpValue class to get the accurate value in ServiceClass
public class ServiceClass {
public Employe getListOfEmp(List<String> criteria) {
Employe emp = new Employe();
EmpValue empValue = new EmpValue();
if (criteria.contains("IT")) {
emp.setEid(empValue.getIt());
}
if (criteria.contains("SALES")) {
emp.setEid(empValue.getSales());
}
if (criteria.contains("SERVICE")) {
emp.setEid(empValue.getService());
}
return emp;
}
}
public class EmpValue {
private String it = "IT-1001";
private String service = "SERVICE-1001";
private String sales = "SALES-1001";
public String getIt() {
return it;
}
public String getService() {
return service;
}
public String getSales() {
return sales;
}
}

First I will do some changes to the code to make it testable.
1: EmplValue object should be passed to the method by the client or should be an instance variable in your Service class. Here I am using it as an instance variable, so that client can set it.
public class ServiceClass {
private EmpValue empValue;
public ServiceClass(EmpValue empValue){
this.empValue = empValue;
}
public Employe getListOfEmp(List<String> criteria) {
Employe emp = new Employe();
if (criteria.contains("IT")) {
emp.setEid(empValue.getIt());
}
if (criteria.contains("SALES")) {
emp.setEid(empValue.getSales());
}
if (criteria.contains("SERVICE")) {
emp.setEid(empValue.getService());
}
return emp;
}
}
I am using Mockito and JUnit to write unit-test for this class .
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
#RunsWith(MockitoJunitRunner.class)
public class ServiceClassTest{
#Test
public void shouldReturnEmployeeWithEidSetWhenCriteriaIsIT(){
// Craete mock and define its behaviour
EmpValue mEmpValue = mock(EmpValue.class);
when(mEmpValue.getIt()).thenReturn("IT-1001");
// Create the object of class user test with mock agent and invoke
ServiceClass serviceClass = new ServiceClass(mEnpValue);
Employee result = serviceClass.getListOfEmp(asList("IT"));
// Verify the result as per your expectation
assertEquals("It-001", result.getEid());
}
}

Related

How to handle objects created within the method under test

I have the following model classes:
#Data
public class Address {
private String street;
private int number;
}
#Data
public class Person {
private String name;
private Address address;
}
and the following services:
#Service
public class MyService {
private final OtherService otherService;
public MyService(OtherService otherService) {
this.otherService = otherService;
}
public void create() {
Person myPerson = new Person();
myPerson.setName("John");
otherService.synchronize(myPerson);
myPerson.getAddress().setNumber(12);
}
}
#Service
public class OtherService {
public void synchronize(Person person) {
Address address = new Address();
address.setStreet("sample street");
address.setNumber(123);
person.setAddress(address);
}
}
I want to write a unit test for MyService. This is the not working version of the test:
#ExtendWith(SpringExtension.class)
class MyServiceTest {
#Mock OtherService otherService;
#InjectMocks MyService myService;
#Test
void test_create() {
// GIVEN
doNothing().when(otherService).synchronize(any(Person.class));
// WHEN
myService.create();
// THEN
verify(otherService).synchronize(any());
}
}
This fails because the myPerson object is created within the method being tested and therefore I get a NullPointerException when running the test. How could I deal with this issue? should I capture the value passed to the otherService?
There's a little complexity but it's not bad. Replace your doNothing call with something like this:
Mockito.doAnswer(
new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws Exception {
Person arg = invocation.getArgument(0);
arg.setAddress(new Address());
return;
}
}).when(otherService).synchronize(any(Person.class));

SpringMVC Mockito Mocking values of class within class

I am trying to use mockito to mock the return value of method3() in class3 when i run the method try() in class1 from the testclass. I have restriction as to not being able to make any edition to the codes that i have. So, i cannot add in constructors to make the mock as per some solutions that i have seen given on the internet. I am using MockMVC with WebApplicationContextSetup. Please guide me if it is possible to mock the value of method3() only using mockito and if it is not possible what is the other solution that i can use to mock the value?
class1
{
Class2 c2 = new Class2();
public String try()
{
Something temp1 = c2.method1();
}
}
class2
{
Class3 c3 = new Class3();
public String method1()
{
return c3.method3();
}
}
class3
{
//Will like to mock the return value of this method
public String method3()
{
return "asd";
}
}
testclass
{
class1 c1 = new class1();
c1.try();
}
Thanks Alot :D
Regarding your code, it looks you need to mock a static method :
return Class3.method3();
Or not
public String method3()
Please precise because the answer will be different depending your need to mock static method or not.
For this you need to Spy your class2.
import org.junit.Before;
import org.junit.Test;
import org.mockito.*;
import static org.junit.Assert.assertEquals;
public class TestClass {
#InjectMocks
private Class1 class1 = new Class1();
#InjectMocks #Spy
private Class2 class2 = new Class2();
#Mock
private Class3 class3;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testWithMock() {
Mockito.when(class3.method3()).thenReturn("mocked");
assertEquals("mocked", class1.doTry());
}
}

Spring + Hibernate + TestNG + Mocking nothing persist, nothing is readed in test

Fighting with TestNG, Spring an Hibernate. I'm writing test for Service class, and it's always failure. But without test class works fine. So App is working, but tests don't want to.
Here is my test class
#Transactional
public class BorrowerServiceTest {
#Mock
BorrowerDAOImpl borrowerDAO;
#InjectMocks
BorrowerService borrowerService;
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void persistTest() {
Borrower borrower = new Borrower.BorrowerBuilder().firstName("Lars").lastName("Urlich").adress("LA")
.phoneNumber("900900990").build();
borrowerService.persist(borrower);
List<Borrower> borrowerList = borrowerService.getBorrowerByName("Lars Urlich");
Assert.assertEquals(true, borrower.equals(borrowerList.get(0)));
}
}
My BorrowerService:
#Service("borrowerService")
#Transactional
public class BorrowerService {
#Autowired
private BorrowerDAO borrowerDAO;
public List<Borrower> getBorrowers() {
return borrowerDAO.getBorrowers();
}
public List<Borrower> getBorrowerByName(String name) {
return borrowerDAO.getBorrowerByName(name);
}
public boolean removeBorrower(Borrower borrower) {
return borrowerDAO.removeBorrower(borrower);
}
public boolean persist(Borrower borrower) {
return borrowerDAO.persist(borrower);
}
}
My BorrowerDAOImpl:
#Repository("borrowerDAO")
#Transactional
public class BorrowerDAOImpl extends DAO implements BorrowerDAO {
#Override
public List<Borrower> getBorrowers() {
List<Borrower> borrowerList = null;
Query query = entityManager.createQuery("SELECT B FROM Borrower B");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public List<Borrower> getBorrowerByName(String name) {
List<Borrower> borrowerList = null;
String[] values = name.split(" ");
Query query = entityManager.createQuery("SELECT B FROM Borrower B WHERE B.firstName LIKE '" + values[0]
+ "' AND B.lastName LIKE '" + values[1] + "'");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public boolean removeBorrower(Borrower borrower) {
String firstName = borrower.getFirstName();
String lastName = borrower.getLastName();
Query query = entityManager
.createQuery("DELETE Borrower where FIRST_NAME LIKE :FirstName AND LAST_NAME LIKE :LastName");
query.setParameter("FirstName", firstName);
query.setParameter("LastName", lastName);
query.executeUpdate();
return true;
}
#Override
public boolean persist(Borrower borrower) {
entityManager.persist(borrower);
return true;
}
}
and abstract DAO:
#Repository
#Transactional
public abstract class DAO {
#PersistenceContext
protected EntityManager entityManager;
}
Maven returns failure:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.LinkedList.checkElementIndex(LinkedList.java:555)
at java.util.LinkedList.get(LinkedList.java:476)
at com.me.service.test.BorrowerServiceTest.persistTest(BorrowerServiceTest.java:41)
I also had to fight with this. The problem here is that your test runs in it's own transaction, so nothing will be committed during method's execution. Now here is what I did:
public class IntegrationTest extends SomeTestBase
{
#Autowired
private PlatformTransactionManager platformTransactionManager;
private TransactionTemplate transactionTemplate;
#Autowired
private BeanToTest beanToTest;
#Override
#Before
public void setup()
{
super.setup();
this.transactionTemplate = new TransactionTemplate(this.platformTransactionManager);
}
#Test
public void fooTest()
{
// given
// when
boolean result = this.transactionTemplate.execute(new TransactionCallback<Boolean>()
{
#Override
public Boolean doInTransaction(TransactionStatus status)
{
return IntegrationTest.this.beanToTest.foo();
}
});
// then
}
}
This allows you to have methods execute within a separate transaction. Please note that you might declare some variables as final.
Hope that helps.
Check the Spring documentation: it looks your test class should extend AbstractTestNGSpringContextTests.
Use #Commit annotation on the whole test class or even method to persist changes made in the test. For more information https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#commit

Spring RestController ignore XmlElement annotation in Wrapper Class

I'm using Spring 4.x and I have following RestController method which should return list of all flights
#RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = RequestMethod.GET)
public FlightWrapper returnAllFlights() {
List<FlightDto> flights = data.findAll();
return new FlightWrapper(flights);
}
FlightWrapper class looks like this (rootElement = flights element = flight):
#XmlRootElement(name = "flights")
public class FlightWrapper {
private List<FlightDto> flights;
public FlightWrapper() {}
public FlightWrapper(List<FlightDto> flights) {
this.flights = flights;
}
#XmlElement(name = "flight")
public List<FlightDto> getFlights() {
return flights;
}
public void setFlights(List<FlightDto> flights) {
this.flights = flights;
}
}
The problem is when I call returnAllFlights() it will return xml in this format:
<FlightWrapper>
<flights>
<flights>
....
</flights>
<flights>
....
</flights>
</flights>
</FlightWrapper>
I expected that single flight should have tag flight and whole list of flights should be flights however as you can see items in list have the same tag as list itself.
Any idea how to fix it ?
According with your comments since you are using jackson-dataformat-xml module the JAXB annotations now are ignored. You must update your class to use these annotations.
#JacksonXmlRootElement(localName="flights")
public class FlightWrapper {
private List<FlightDto> flights;
public FlightWrapper() {}
public FlightWrapper(List<FlightDto> flights) {
this.flights = flights;
}
#JacksonXmlElementWrapper(useWrapping=false)
#JacksonXmlProperty(localName="flight")
public List<FlightDto> getFlights() {
return flights;
}
public void setFlights(List<FlightDto> flights) {
this.flights = flights;
}
}
I had the same problem than you but through Spring Framework, not through Spring Boot. But that behaviour happens when the jackson-dataformat-xml module is added into the classpath. It according with my experience.

How to custom #FeignClient Expander to convert param?

Feign default expander to convert param:
final class ToStringExpander implements Expander {
#Override
public String expand(Object value) {
return value.toString();
}
}
I want custom it to convert user to support GET param, like this
#FeignClient("xx")
interface UserService{
#RequestMapping(value="/users",method=GET)
public List<User> findBy(#ModelAttribute User user);
}
userService.findBy(user);
What can i do?
First,you must write a expander like ToJsonExpander:
public class ToJsonExpander implements Param.Expander {
private static ObjectMapper objectMapper = new ObjectMapper();
public String expand(Object value) {
try {
return objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new ExpanderException(e);
}
}
}
Second, write a AnnotatedParameterProcessor like JsonArgumentParameterProcessor to add expander for your processor.
public class JsonArgumentParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<JsonArgument> ANNOTATION = JsonArgument.class;
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation) {
MethodMetadata data = context.getMethodMetadata();
String name = ANNOTATION.cast(annotation).value();
String method = data.template().method();
Util.checkState(Util.emptyToNull(name) != null,
"JsonArgument.value() was empty on parameter %s", context.getParameterIndex());
context.setParameterName(name);
if (method != null && (HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method) || HttpMethod.DELETE.matches(method))) {
data.formParams().add(name);
} else {
`data.indexToExpanderClass().put(context.getParameterIndex(), ToJsonExpander.class);`
Collection<String> query = context.setTemplateParameter(name, data.template().queries().get(name));
data.template().query(name, query);
}
return true;
}
}
Third,add it to Feign configuration.
#Bean
public Contract feignContract(){
List<AnnotatedParameterProcessor> processors = new ArrayList<>();
processors.add(new JsonArgumentParameterProcessor());
processors.add(new PathVariableParameterProcessor());
processors.add(new RequestHeaderParameterProcessor());
processors.add(new RequestParamParameterProcessor());
return new SpringMvcContract(processors);
}
Now, you can use #JsonArgument to send model argument like:
public void saveV10(#JsonArgument("session") Session session);
I don't know what #ModelAttribute does but I was looking for a way to convert #RequestParam values so I did this:
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import org.springframework.cloud.netflix.feign.FeignFormatterRegistrar;
import org.springframework.format.FormatterRegistry;
import org.springframework.stereotype.Component;
import static com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat.E164;
#Component
public class PhoneNumberFeignFormatterRegistrar implements FeignFormatterRegistrar {
private final PhoneNumberUtil phoneNumberUtil;
public PhoneNumberFeignFormatterRegistrar(PhoneNumberUtil phoneNumberUtil) {
this.phoneNumberUtil = phoneNumberUtil;
}
#Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(Phonenumber.PhoneNumber.class, String.class, source -> phoneNumberUtil.format(source, E164));
}
}
Now stuff like the following works
import com.google.i18n.phonenumbers.Phonenumber;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
#FeignClient("data-service")
public interface DataClient {
#RequestMapping(method = RequestMethod.GET, value = "/phoneNumbers/search/findByPhoneNumber")
Resource<PhoneNumberRecord> getPhoneNumber(#RequestParam("phoneNumber") Phonenumber.PhoneNumber phoneNumber);
}
As the open feign issue and spring doc say:
The OpenFeign #QueryMap annotation provides support for POJOs to be used as GET parameter maps.
Spring Cloud OpenFeign provides an equivalent #SpringQueryMap annotation, which is used to annotate a POJO or Map parameter as a query parameter map since 2.1.0.
You can use it like this:
#GetMapping("user")
String getUser(#SpringQueryMap User user);
public class User {
private String name;
private int age;
...
}

Resources