JUnit Mockito with H2 Database - java

I am trying to write JUnit for spring boot controller using Mockito framework. I have injected the service class. I am using embedded h2 database.
When i tried to debug the written test case, I could see that it doesn't invoke the implementation class of the service method and returns empty array. I have attached debug screenshot of the controller class.
Below is the written JUnit class file:-
package com.testSpringBoot;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.testSpringBoot.controller.EmployeeController;
import com.testSpringBoot.dto.EmployeeDTO;
import com.testSpringBoot.repository.EmployeeRepository;
import com.testSpringBoot.service.EmployeeService;
import junit.framework.Assert;
#RunWith(SpringRunner.class)
#SpringBootTest
public class EmployeeControllerJUnit {
private MockMvc mockMvc;
#Mock
private EmployeeService employeeService;
#InjectMocks
private EmployeeController employeeController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build();
}
#Test
public void employeeControllerTest() throws Exception {
Mockito.when(employeeService.getAllEmployees()).thenReturn(new ArrayList<EmployeeDTO>());
mockMvc.perform(MockMvcRequestBuilders.get("/employee/getAllEmployees"))
.andExpect(status().isOk());
}
}
Debug Screenshot at controller file:-
Debug screenshot
Could you please let me know where i am making mistake.
Thanks

#Mock
private EmployeeService employeeService;
This instructs Mockito JUnit runner to create a mock of EmployeeService type. A mock does not use any implementation that you could write in your EmployeeService class (or in any of its implementations if EmployeeService is an interface). Instead, it just 'mocks' the behavior of that class.
Here, you instruct the mock to return an empty list when its getAllEmployees() method is called:
Mockito.when(employeeService.getAllEmployees()).thenReturn(new ArrayList<EmployeeDTO>());
And according to your description, it does just that.
You need to decide whether you need to mock your service or not. If not, don't use Mockito's mocks and just use your service implementation (although this will convert your test to an integration test).

Related

Spring Boot Unit Test returns 404 instead of 200

I am new in springboot. I am just watching the Spring in Action and programming follow the author.
then things get difficult when i just reading the chapter 1. I need to test a controller. the code in this book is:
package tacos;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#WebMvcTest(HomeController.class) // <1>
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc; // <2>
#Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) // <3>
.andExpect(status().isOk()) // <4>
.andExpect(view().name("home")) // <5>
.andExpect(content().string( // <6>
containsString("Welcome to...")));
}
}
I am using springboot 2.7.3 so i just remove #RunWith(SpringRunner.class) from my code. and i immediately get an error when do the test:
after google that i realized it might not find my controller so I add #Import(HomeController.class) to the test class. It looks like:
package com.qph.tacos;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest(HomeControllerTest.class)
#Import(HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("Welcome to...")));
}
}
then it just pass the test!!!
now i just wonder why the code written by the author can pass the test without #Import(HomeController.class)?
my directory structure is like:
the controller to be tested is:
package com.qph.tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
// return the name of template
return "home";
}
}
I am new in springboot. maybe it's a simple question but i really spent so much time on it.
Thanks for your help!
Change to
#WebMvcTest(HomeController.class)
You should reference the controller under test with this annotation, otherwise it won't be loaded into the application context.

NullPointerException in injectedMock

I have a RestController that I want to test:
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PetController implements PetApi {
#Autowired
PetRepository pr;
#Override
public ResponseEntity<Pet> addPet(#Valid Pet pet) {
pr.save(new PetBE(9L, "dummy"));
return new ResponseEntity<Pet>(pet, HttpStatus.OK);
}
}
import org.springframework.data.repository.CrudRepository;
public interface PetRepository extends CrudRepository<PetBE, Long> {
}
I want to mock PetRepository and test if the object passed is the object returned:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#SpringBootTest
public class PetControllerTest {
#InjectMocks
private PetController petController;
#MockBean
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
/*L35*/ ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
When I run this method as a gradle test, I get
com.example.petstore.backend.api.implementation.PetControllerTest > testAddPet() FAILED
java.lang.NullPointerException at PetControllerTest.java:35
which is petController.addPet(p1);.
My printlns in addPet are not displayed and I can't set any breakpoints there because it is mocked. The only reference in addPet that could be null is pr, but it works fine when I send a request with curl.
I've also tried adding
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
because it was suggested here but that gave an InitializationException:
com.example.petstore.backend.api.implementation.PetControllerTest > initializationError FAILED
org.junit.platform.commons.JUnitException at LifecycleMethodUtils.java:57
How can I debug this?
How can I get this to work?
You're mixing annotations from various testing frameworks here. If you wish to use the Mockito annotation #InjectMocks then I'd recommend not using any Spring-related mocking annotations at all, but rather the #Mock annotation to create a mocked version of the bean you want to inject (into the #InjectMocks-annotated field). Also make sure you bootstrap the Mockito extension with #ExtendWith(MockitoExtension.class). Something like:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#ExtendWith(MockitoExtension.class)
public class PetControllerTest {
#InjectMocks
private PetController petController;
#Mock
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
EDIT: Calling MockitoAnnotations.initMocks(this) inside for example a #BeforeEach-annotated method is necessary if you don't want to use the MockitoExtension. They're essentially the same thing, but it's less necessary in JUnit Jupiter because you can extend a test class with multiple extensions, which was not possible in JUnit 4.x. So if you wanted to bootstrap your test with both a Spring context and Mockito, then you had to pick one of them and setup the other one yourself.

Eclipse Mockito debugging; Doesn't stop on breakpoints in spied beans

I have a problem using Eclipse to debug Mockito based unittests. Tests are working fine but I cannot step in to spied code or stop on any breakpoint. Debugger is working fine in IntelliJ, but not in Eclipse.
I put a simple unittest to demonstrate my problem. I have JUnit test for SomeService which calls AnotherService. I'm using Mockito to mock output of AnotherService
SomeService.java
package com.example.my;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class SomeService {
#Autowired
private AnotherService anotherService;
public void callBoom() {
System.out.print(anotherService.boom());
}
}
AnotherClass.java
package com.example.my;
import org.springframework.stereotype.Service;
#Service
public class AnotherService {
public String boom() {
return "boom";
}
}
SomeServiceTest.java
package com.example.my;
import static org.mockito.Mockito.doReturn;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
class SomeServiceTest {
#Spy
#InjectMocks
private SomeService someService;
#Mock
private AnotherService anotherService;
#BeforeEach
void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("BOOM!!!").when(anotherService).boom();
}
#Test
void testBoom() {
someService.callBoom();
}
}
I can see "BOOM!!!" printed, so mock is working. But if I put breakpoint into callBoom, Eclipse never stop on it. For other tests it doesn't stop at any method of "spied" beans. When you have larger unittest inability to debug it is very frustrating. I'm running Eclipse 2019-09 with JDK 1.8.0.221, but tried it on 2020-03 as well.

NoClassDefFoundError: org/springframework/web/bind/MissingMatrixVariableException -- testing repositories/interfaces

Here's the problem: I keep running into the MissingMatrixVariableException in my stacktrace when attempting to test my Repository interfaces. I've got 5 and want to increase my overall code coverage.
Be nice - I'm a noob. I've attempted to Mock after doing my own research, and it's been a bit overwhelming.
I've tried Mocking the repository and instantiated my MockMvc mockMvc variables. I've setUp the MockitoAnnotations.initMocks(this);
I run the test and for any testing I've done, I've hit the MissingMatrixVariableException error.
I've found nothing in Stack / Google searches for this Exception when testing repositories.
package example.repository;
import example.model.Metrics;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
#EnableJpaRepositories
public interface MetricsRepository extends JpaRepository<Metrics, Long> {
List<Metrics>findByDate(String date);
#Modifying
#Transactional
#Query(nativeQuery = true, value = "delete from backlogreport.metrics where date = ?1")
void deleteDate(String date);
}
This is my test class:
package example.repository;
import example.controller.RestApiController;
import example.model.Metrics;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import java.util.Collections;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
public class MetricsRepositoryTest {
#InjectMocks
private RestApiController restApiController;
#Mock
private MetricsRepository MetricsRepository;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(restApiController).build();
}
#Test
public void shouldGetByDate() throws Exception {
Metrics Metrics = new Metrics();
Metrics.setDate("2019-04-01");
Metrics.setAgeLength("20 days old");
when(MetricsRepository.findAll())
.thenReturn(Collections.singletonList(Metrics));
mockMvc.perform(get("/report/2019-04-01"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(print());
}
}
What I'm trying to achieve is when the mockMvc is setup, I put in the date which accepts a String ("2019-04-01") and an age length of ("20 Days").
I then perform my urL "get" and expect the status is ok, then print... except I keep hitting this MissingMatrixVariableException. Any ideas?

JUnit Mock test returns null on using InjectMocks

I am trying to unit test my service class but the mock returns null
package com.tgt.store.pricetask.pricetask_service;
import com.tgt.store.pricetask.model.PriceTaskMaster;
import com.tgt.store.pricetask.model.TaskModel;
import com.tgt.store.pricetask.repository.PriceTaskMasterRepository;
import com.tgt.store.pricetask.service.DataMigrationService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.time.LocalDateTime;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
#RunWith(SpringJUnit4ClassRunner.class)
public class DataMigrationServiceTest {
#Mock
PriceTaskMasterRepository priceTaskMasterRepository;
#InjectMocks
DataMigrationService dataMigrationService;
#Test
public void
testPriceTaskMasterService_whenTaskModelPassed_thenSavePriceTaskMaster() {
TaskModel taskModel = new TaskModel.TaskModelBuilder().setTaskID(1)
.setDueDate("2017-11-01T11:41:00+0000").setIsAlertable("A").setIsPriority("P").setLocationid("1234")
.createTaskModel();
PriceTaskMaster priceTaskMaster = new PriceTaskMaster.PriceTaskMasterBuilder().setId(1L).setTaskStatus("A")
.setAlertable("A").setPriority("P").setLocationId(1234)
.setDueDate(LocalDateTime.now()).createPriceTaskMaster();
when(priceTaskMasterRepository.insertPriceTaskMaster(any(PriceTaskMaster.class))).thenReturn(priceTaskMaster);
PriceTaskMaster savedPriceTaskMaster = dataMigrationService.savePriceTaskMaster(taskModel);
assertNotNull(savedPriceTaskMaster);
assertEquals("A", savedPriceTaskMaster.getTaskStatus());
assertEquals("P", savedPriceTaskMaster.getPriority());
assertEquals(1234, savedPriceTaskMaster.getLocationId().intValue());
assertEquals(123456789, savedPriceTaskMaster.getTcin().longValue());
verify((priceTaskMasterRepository), times(1)).insertPriceTaskMaster(priceTaskMaster);
}
}
In the above code, when call reaches the service class priceTaskMasterRepository is null. I am unable to figure out if I am missing something. I have tried RunWith SpringRunner and MockitoJUnitRunner but same result. Any help is appreciated.
Thank you in advance.
You will need to initialize the DataMigrationService field when using the #InjectMocks annotation. That will create an instance of the class under test as well as inject the mock objects into it.
#InjectMocks
DataMigrationService dataMigrationService = new DataMigrationService();
http://www.baeldung.com/mockito-annotations
If you're using JUnit to run your tests, then you should have something like this:
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
}
This ensures all your mocks are initialised. This is supposed to be unnecessary when using MockitoJUnitRunner.

Categories

Resources