Testing service layer in Spring Boot using Mockito - spring-boot

I am trying to unit test my service layer using mockito having a connection to a Database through the Repository layer.
My test case:
#InjectMocks
private EmployeeService employeeService;
#Mock
private EmployeeRepository employeeRepository;
#Test
public void getActionFromEmployeeIdTest() throws ActionException {
//setup
when(employeeRepository.getActionsByEmployeeId(anyLong()).thenReturn(EmployeeEntity);
// exercise
List<Employee> result = employeeService.getActionsByEmployeeIdService(101);
//verify
assertEqual(EmployeeEntity, result);
}
Service Layer:
#Service
public class EmployeeService {
#Override
public List<EmployeeUser> getActionsByEmployeeIdService(long employeeId) {
Employee employee = employeeRepository.findByEmployeeIdId(employeeId);
List<EmployeeUser> actions = employeeUserRepository.getActionsByEmployeeId(employeeId);
return actions;
}
Repository layer:
#Repository
public interface EmployeeUserRepository extends JpaRepository<EmployeeUser,Long> {
#Transactional
#Query(value = "Select e from EmployeeUser e where e.employeeId = :employeeId" )
List<EmployeeUser> getActionsByEmployeeId(#Param("employeeId") long employeeId);
}
I am using #InjectMocks for employeeService and #Mock for employeeRepository.
result seems to be returning null. Is there anyway I can return a non-null value in my test?

You are using reference of employeeRepository to call getActionssByEmployeeId(anyLong()) method.
when(employeeRepository.getActionssByEmployeeId(anyLong()).thenReturn(EmployeeEntity);
But in service calss this methos is getting called by employeeUserRepository
List<EmployeeUser> actions = employeeUserRepository.getActionsByEmployeeIdId(employeeId);
Please check this.

Related

Mockito injecting mocks Spring Boot test

Hi I have a service class that contains mapper and repository:
#Service
public class ProductServiceImp implements ProductService {
#Autowired
private ProductRepository repository;
#Autowired
private WarehouseApiMapper mapper;
public ProductServiceImp(ProductRepository repository) {
this.repository = repository;
}
}
Repository:
#Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
}
Mapper:
#Mapper(componentModel = "spring")
public interface WarehouseApiMapper {
WarehouseApiMapper mapper = Mappers.getMapper(WarehouseApiMapper.class);
Product ProductDtoToProduct(ProductDto productDto);
ProductDto ProductToProductDto(Product product);
}
In test class I would like to inject mock repository and autowired mapper
Here is my test class:
#SpringBootTest
public class ProductServiceTest {
#Mock
ProductRepository repository;
#InjectMocks
ProductServiceImp service;
#ParameterizedTest
#MethodSource("provideParametersProductUpdate")
void assert_that_product_is_updated_correctly(String productName, BigDecimal productPrice) {
Product oldProduct = new Product("Product that does not exist", BigDecimal.valueOf(1000000), null);
oldProduct.setId(1);
Mockito.when(repository.findById(1)).thenReturn(Optional.of(oldProduct));
Product newProduct = new Product(productName, productPrice, null);
newProduct.setId(1);
ProductDto updatedProduct = service.updateProduct(newProduct);
Assertions.assertEquals(productPrice, updatedProduct.getPrice());
Assertions.assertEquals(productName, updatedProduct.getName());
}
private static Stream<Arguments> provideParametersProductUpdate() {
return Stream.of(
Arguments.of("dark chocolate", BigDecimal.valueOf(3.2)),
Arguments.of("chewing gum", BigDecimal.valueOf(1.2)),
Arguments.of("lollipop", BigDecimal.valueOf(4.0))
);
}
}
Code throws NullPointerException when is trying to map object in service method.
Somebody knows how can I inject this? Thanks for ur answers
If you want to create just a Mockito test you could use the annotation #RunWith(MockitoJUnitRunner.class) instead of #SpringBootTest.
But if you want to create a Spring Boot integration test then you should use #MockBean instead of #Mock and #Autowired instead of #InjectMocks.

Spring Boot Service Junit Mockito issue

I have written Junit for the service. mocking dao.
Service method return type in EmployeeDTO.
Dao return type is Employee.
problem is employee to employeeDto conversion failed in test case.
when(dao.method()).thenReturn(new Employee), so on call od service.method() I am facing issue since dozer is in between to convert employee to employeedto in the actual code.
any suggestions to fix this.
#SpringBootTest(classes = { EmployeeSearchService.class, EmployeeDao.class })
public class EmployeeSearchServiceTest {
#Mock // will create a mock implementation for the EmployeeDao
EmployeeDao employeeDao;
#InjectMocks // will inject the mocks marked with #Mock to this instance when it is created.
private EmployeeSearchService employeeSearchService ;
#Mock
private DozerBeanMapper dozerBeanMapper;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#DisplayName("fetchEmployees with valid data")
#Test
public void testfetchEmployeesWithValidData() throws IOException {
when(employeeDao.fetchEmployees()).thenReturn(Stream
.of(new Employee((long) 1, "James", "Java", "Manager", "Google"),
new Employee((long) 2, "Richard", "C++", "Manager", "Microsfot"))
.collect(Collectors.toList()));
//when(dozer.map(Mockito.any(), Mockito.any())).thenReturn(employeeDTO);
System.out.println(employeeSearchService.fetchEmployees());
assertEquals(4, employeeSearchService.fetchEmployees().size());
}
}
#Service
public class EmployeeSearchServiceImpl implements EmployeeSearchService {
#Autowired
EmployeeDao employeeDao;
#Autowired
DozerBeanMapper dozerBeanMapper;
#Override
#Logging
public List<EmployeeDTO> fetchEmployees() throws IOException {
List<Employee> aEmployeeList = employeeDao.fetchEmployees();
List<EmployeeDTO> aEmployeeDTOList= aEmployeeList.stream().map(emp ->
dozerBeanMapper.map(emp,
EmployeeDTO.class)).collect(Collectors.toList());
if (aEmployeeList.isEmpty()) {
throw new EmployeeNotfoundException("Employee Details Not Available");
}
return aEmployeeDTOList;
}
}
#Repository
public class EmployeeDaoImpl implements EmployeeDao {
#Override
#Logging
public List<Employee> fetchEmployees() throws IOException {
List<String> aFileList=fileUtils.getFileContent(EmployeeSearchConstants.EMPLOYEE_DETAILS_PATH);
List<Employee> aEmployeList = getEmployee(aFileList);
if (aEmployeList.isEmpty()) {
throw new EmployeeNotfoundException("Employee Details Not Available");
}
return aEmployeList;
}
}
If I understand you correctly your issue is that you are looking for a way to convert your Employee into a EmployeeDTO object, which in your code is done using dozerBeanMapper.map(emp, EmployeeDTO.class).
One option would be to change the EmployeeSearchServiceImpl and use Constructor Injection instead of Field Injection. This way you could simply use the real dozer class to do the mapping (by manually passing the mock for employeeDao and the real dozerBeanMapper).
Constructor Injection is done by moving the #Autowired to the constructor instead of the fields. Depening on your spring version and in case you only have one constructor for the class you might be able to omit the annotation. For more information check here.
EmployeeDao employeeDao;
DozerBeanMapper dozerBeanMapper;
#Autowired
public EmployeeSearchServiceImpl(EmployeeDao employeeDao, DozerBeanMapper dozerBeanMapper) {
this.employeeDao = employeeDao;
this.dozerBeanMapper = dozerBeanMapper;
}
Another option would be to use Mockito's thenAnser functionality. However
you still require something to do the conversion for you.
when(dozerBeanMapper.map(Mockito.any(), Mockito.any())).thenAnswer(new Answer<EmployeeDTO>() {
public EmployeeDTO answer(InvocationOnMock invocation) {
Employee employee = (Employee) invocation.getArguments()[0];
// convert to EmployeeDTO
EmployeeDTO dto = ...
return dto;
}
});

How to mock Spring Data and unit test service

I'm trying to unit test a service method. The service methods calls a spring data repository method to fetch some data. I want to mock that repository call, and supply the data myself. How to do that? Following Spring Boot documentation, when I mock the repository and call the repository method directly in my test code, the mock is working. But when I call the service method, which in turn would call the repository method, mocking isn't working. Below is the sample code:
Service class:
#Service
public class PersonService {
private final PersonRepository personRepository;
#Autowired
public PersonService(personRepository personRepository) {
this.personRepository = personRepository;
}
public List<Person> findByName(String name) {
return personRepository.findByName(name); // I'd like to mock this call
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
For Spring Data repositories you need to specifiy the bean name. Mocking via type doesn't seem to work because the repository is a dynamic proxy at runtime.
The default bean name for PersonRepository is "personRepository", so this should work:
#MockBean("personRepository")
private PersonRepository personRepository;
Here's the complete test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean("personRepository")
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
Probably the repository is marked with #MockedBean annotation. I do not know if Spring can auto wire by type if the repository is a mock.
You can define the #Bean method and return Mockito.mock(X.class), this should work.
Not sure you need spring for unit testing a service method though. A lighter approach would be to use solely Mockito with its #InjectMocks annotation.

Unit testing field validation with MockMvc but without Spring context?

Is it possible to test #Valid annotations using MockMvc and MockitoJUnitRunner? I can test most of the behavior of my CRUD controller but validation seems to require the use of Spring's JUnit runner, building the entire context and creating the JPA repo implementation which requires a lot of stuff.
The test below tries to test a POST method receiving a Customer entity where the firstName field is annotated with #Size(min=2, max=20). The result is
java.lang.AssertionError: View name expected:<edit> but was:<redirect:/info>
So the validation did not run.
#RunWith(MockitoJUnitRunner.class)
public class DataControllerTest {
#Mock
CustomerRepository mockRepo;
#InjectMocks
private DataController controller;
MockMvc mockmvc;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockmvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testBadSubmit() throws Exception {
mockmvc.perform(MockMvcRequestBuilders.post("/edit/1")
.param("firstName", "a"))
.andExpect(MockMvcResultMatchers.view().name("edit"));
Mockito.verifyZeroInteractions(mockRepo);
}
}
Controller class:
#Controller
public class DataController {
#Autowired
public CustomerRepository crep;
...
#RequestMapping(value = {"/edit/{id}"}, method = RequestMethod.POST)
public String add(Model model, #Valid Customer customer, Errors result) {
if (result.hasErrors()) {
return "edit";
}
crep.save(customer);
return "redirect:/info";
}
Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id = null;
#Column(length=20)
#Size(min=2, max=20)
private String firstName;
...
}
JPA Repository interface:
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
The purpose of the SpringJUnit4ClassRunner is to automatically load the application context and wire up everything automatically. You should be able to use MockitoJUnitRunner but you'll have to load the application context you want to use manually in your test. Regardless, however, you do need to load an application context, because calling DataController#add() through Spring is the only way that the #Valid annotation will be processed.
EDIT: If the real problem here is loading the JPA repository, you can use MockitoJUnitRunner and just load a test application context where a mock JPA repository is manually wired in, at little to no expense.

Issue with transactions in multiple services (Spring Framework/JTA): org.hibernate.ObjectDeletedException: deleted instance passed to merge

I receive the following exception during program execution:
org.hibernate.ObjectDeletedException: deleted instance passed to merge: [ns.entity.Category#<null>]; nested exception is java.lang.IllegalArgumentException: org.hibernate.ObjectDeletedException: deleted instance passed to merge: [ns.entity.Category#<null>]
The following code throws exception:
importer.foo();
Importer service:
#Service
#Transactional
public class Importer {
#Autowired
private UserService userService;
#Autowired
private CategoryService categoryService;
#Transactional
public void foo() {
User user = userService.findByLogin("max");
categoryService.delete(user.getCategories());
}
}
UserService (uses CrudRepository):
#Service
#Repository
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository repository;
#Override
#Transactional(readOnly = true)
public User findById(Long userId) {
return repository.findOne(userId);
}
}
CategoryService (uses CrudRepository):
#Service
#Repository
#Transactional
public class CategoryServiceImpl implements CategoryService {
#Autowired
private CategoryRepository repository;
#Override
#Transactional
public void delete(Set<Category> categories) {
repository.delete(categories);
}
}
The following code snippet in CategoryServiceImpl.delete() works without exception:
for (Category category : categories) {
Category newCat = findById(category.getCategoryId());
if (newCat != null) {
delete(newCat);
}
}
From what I understand two different transactions are used (one read only and one for deletion). Is it possible to re-use the transaction for all calls? Removing (readOnly = true) from UserServiceImpl.findById() does not help.
I thought that just one transaction should be used for all three methods (Importer.foo(), UserServiceImpl.findById(), CategoryServiceImpl.delete()) according to Spring documentation.

Resources