below is my controller:
#RestController
#RequestMapping("/Employee")
public class Employeecontroller {
#Autowired
Employeeservice empservice;
#PostMapping("/addEmployee") // adding emp details into the table
public Employee addemployee(#RequestBody Employee emp)
{
empservice.saveemp(emp);
return emp;
}
}
this is the empservice class:
#Service
public class Employeeservice {
#Autowired
EmployeeRepository emprepo;
public Employee saveemp(Employee emp) {
System.out.println("inside save emp");
return emprepo.save(emp);
}
}
(here i dont want to call to emprepo.save(emp) method, which is a database call, so i used Mockito.when and then methods in below test class)
below is the test class:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#AutoConfigureMockMvc
class RestServiceApplicationTests {
#Autowired
private MockMvc mvc;
#Autowired
ObjectMapper objectMapper;
#MockBean
Employeeservice empservice;
Employee reqemp = new Employee("Bob", "java");
#Test
public void testaddemp() throws Exception {
when(empservice.saveemp(reqemp)).thenReturn(new Employee(1, "Bob", "java"));
RequestBuilder request = MockMvcRequestBuilders.post("/Employee/addEmployee")
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(reqemp));
MockHttpServletResponse returnedemp = (MockHttpServletResponse) mvc.perform(request).andExpect(status().isOk())
.andReturn().getResponse();
Employee expectedemp = new Employee(1, "Bob", "java");
assertEquals(objectMapper.writeValueAsString(expectedemp), returnedemp.getContentAsString());
}
}
Testcase failed with:
org.opentest4j.AssertionFailedError: expected: <{"id":1,"name":"Bob","tech":"java"}> but was: <{"id":0,"name":"Bob","tech":"java"}>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
at com.easytocourse.demo.RestServicewithActuatorApplicationTests.testaddemp(RestServicewithActuatorApplicationTests.java:55)
when i use #Mock or #SpyBean it is returning expected employee object
Please help me in understanding why #MockBean is not working?
Please clarify the below points
1. what is the difference between #Mock, #MockBean, #SpyBean, #InjectMock annotations, when to use these annotations?
#MockBean is a feature of Spring Boot Test.
Thus you need to remove the #RunWith(MockitoJUnitRunner.class) declaration which is using Mockito's JUnit 4 Runner.
If you want to use JUnit 4, you must use the SpringRunner via #RunWith(SpringRunner.class).
If you want to use JUnit Jupiter (part of JUnit 5), you'll need to use the SpringExtension via #ExtendWith(SpringExtension.class).
Depending on the version of Spring Boot Test that you are using, you may be able to exclude the #ExtendWith(SpringExtension.class) declaration, since recent versions of #SpringBootTest automatically register the SpringExtension for you.
Related topic: Difference between #Mock, #MockBean and Mockito.mock()
Related
I am creating tests for my Springboot application. The application through a Get call, passing my "CarRequest" in the RequestBody
public class CarsRequest implements Serializable {
private String name;
private String plate ;
private String price;
}
it gives me back the car specifications related to that data
{
"name":"",
"plate":"",
"price":"",
"brand":"",
"kilometers":"",
"revisiondate":"",
"owner":""
}
I did this simple test using Mockito but I don't understand why my service is set by default to null, this throws everything in NullPointErexception
public class CarTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CarService service;
#Autowired
ObjectMapper objectMapper;
#Test
public void TestOk() throws Exception{
CarsRequest carsRequest = new CarsRequest();
Car car = new Car();
List<Car> cars = new ArrayList<>();
//septum the fields of cars and add them to the list
cars.add(car);
Mockito.when(
service.getByPlate("bmw",
"TG23IO", "1500")).thenReturn(cars);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/garage/cars").accept(
MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "{name:"bmw","plate":"TG23IO","price":"1500","brand":"POL","kilometers":"80000","revisiondate":"2016-03-15","owner":"JohnLocke"}";
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
Below I also add my CarService
#Service
public class CarService {
#Autowired
CarRepository carRepository;
#Autowired
ObjectMapper objectMapper;
public List<Cars> getByContratto(String name, String plate, String price) throws JsonProcessingException {
//some code, extraction logic
return cars;
}
}
The application works perfectly, only the test does not work. Being a novice in test writing I can't figure out what the null on my Carservice is due to.
If needed, I can include the Controller Get and the repository as well, but I don't think they can help
I believe you are trying to test the controller. Hence include the following annotations on top of the test class.
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
Also, I can see that in the test code CarService is being referred while the service code that is shared contains DocumentService.
Try adding #RunWith(SpringRunner.class) to your test class.
I suspect that you are using Junit 4 for your tests. In Junit 4 you need to add
#RunWith(SpringRunner.class) in your test otherwise all annotations will be ignored.
For more info check the docs here . 46.3 Testing Spring Boot Applications is the section that answers your question.
I would however recommend you to migrate to Junit 5 if possible.
#RunWith(SpringRunner.class)
public class CarTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CarService service;
#Autowired
ObjectMapper objectMapper;
#Test
public void TestOk() throws Exception{
// code here
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
Assuming, you want to test just only your controller layer (Because you are using mockMvc) and you are using Junit 4 (looks like from your code), you should write your test case as follows
#RunWith(SpringRunner.class)
#WebMvcTest(SomeController.class)
public class CarTest {
#MockBean
private SomeService service;
#Autowired
private MockMvc mockMvc;
#Test
public void shouldPassTest() {
BDDAssertions.assertThat(service).isNotNull();
}
}
Note: In example test method is irrelevant, I added just to to illustrate.
Let us know if it helps.
I'm having a problem injecting mock into one class I need for testing. I'm trying to mock a Dao class and had no problem doing so using ReflectionTestUtils in various services I'm using, however this one just does not want to work, it keeps calling the Dao class and getting errors from the database.
This is the test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class DedicationControllerTest extends AbstractRestTest {
#Mock
UserDaoImpl userDao;
#Autowired
#InjectMocks
GrantedAuthoritiesLevelsHolder grantedAuthoritiesLevelsHolder;
#Test
public void shouldTest() throws Exception {
//given
String json = this.getJsonFromFile("json/my.json");
Mockito.when(userDao.getUser(Mockito.anyString())).thenReturn(new User(1l, "mock"));
ReflectionTestUtils.setField(grantedAuthoritiesLevelsHolder, "userDao", userDao);
ResultActions result = mockMvc.perform(post( controllerUrl + "/action")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json));
// then
result
.andExpect(status().isOk());
}
}
And this is the class I'm trying to inject mock into:
#Component
#Scope(value="session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class GrantedAuthoritiesLevelsHolder {
#Autowired
private UserDao userDao;
// some methods
}
You will have to register mocked bean as UserDao when the context is getting loaded. You can register it as shown below. Put this in any class annotated with #Configuration
#Bean
#Primary
public UserDao UserDao() {
return mock(UserDao.class);
}
I believe that your configuration may be not enough to put a mock into Spring context.
My advice:
#MockBean(answer=Answers.RETURNS_SMART_NULLS)
UserDao userDao;
#Autowired
GrantedAuthoritiesLevelsHolder grantedAuthoritiesLevelsHolder;
It should put a mock into Spring context, moreover it should give you hints with incorrect/missing stubbing.
I would like to test that my custom exception is thrown when I call a method like that:
private MyObject queryDatabase(String request, String end){
HttpEntity myEntity = new NStringEntity(request, ContentType.APPLICATION_JSON)
Response response = restClient.performRequest("GET", end,Collections.emptyMap(), myEntity)
MyObject myObject = mapper.readValue(response.getEntity().getContent(), MyObject.Class);
if(myObject.getFunctions().isEmpty()){
throw new EmptyResultException("Empty result");
}
return myObject;
}
My EmptyResultException Class :
#ResponseStatus(value=HttpStatus.NO_CONTENT)
public Class EmptyResultException() extends RuntimeException(){
...
}
I'm just starting with JUnit and I have tried :
#Mock MyService myservice;
#Test(expected=EmptyResultException.class)
public void shouldReturnException() {
when(myService.queryDatabase(anyString(),anyString())).thenReturn(new MyObject());
}
It's not working. The behavior I want to try is in private method. It seems I should use Powermock for such cases. However, I have read it is not good to test private method so I would like to change my method to public. Any help please?
EDIT : My full implementation :
I have a Restcontroller :
#RestController
#RequestMapping...
public class MyController {
#Autowired
MyService myService;
#RequestMapping...
public MyObject findObject(#Valid DtoParameters parameters){
return myService.executeQuery(parameters);
}
}
And in myService Class :
#Service
public class MyService{
public MyObject executeQuery(DtoParameters parameters){
return
queryDatabase(parameters.buildRequest(),parameters.buildEnd());
}
}
If your class under test is MyService, it shouldn't have a #Mock.
I don't know your whole implementation, but let's assume MyService have injected dependencies (like your mapper)
First I would mock the mapper and inject it in MyService:
#Mock
private ObjectMapper mapper;
#InjectMocks
private MyService myService;
For this to work, your test class need to use the mockito runner. You can annotate your class with this:
#RunWith(MockitoJUnitRunner.class)
The mocks will be injected into MyService.
Now you can mock your mapper to return an empty object:
when(mapper.readValue(any(),any())).thenReturn(new MyObject());
myService.queryDatabase("request", "end")
when calling queryDatabase, this should call the mock inside.
If you were trying to test your rest controller, it is a different way. What I suggested is a unit test on MyService. If you want to test the controller, you should do it with a SpringRunner, SpringBootTest and MockMvc. But first, unit test your service, and add other tests on your controller later. For the controller, you would annotate MyService with #MockBean and throw, or Autowire it and #MockBean the mapper inside to return an empty object like the unit test.
Check this: https://spring.io/guides/gs/testing-web/
old answer:
You could change your method to protected, assuming your test is in the same package as the class you are testing, you will be able to mock it.
To mock your method, you need to write it like this:
when(myService.queryDatabase(anyString(),anyString())).thenThrow(EmptyResultException.class);
You forgot to use the when method to wrap your call.
I'm trying to write a simple unit test for a service in Spring Boot.
The service calls a method on a repository which returns an instance of User.
I'm trying to mock the repository, because I want to test only the service.
So, the code for Repository:
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
Service interface:
public interface UserService {
#Async
CompletableFuture<User> findByEmail(String email) throws InterruptedException;
}
Service implementation:
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterruptedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Unit Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#InjectMocks
UserService userService;
#Mock
UserRepository mockUserRepository;
#Before
public void setUp() {
MockitoAnnotations.initMock(this);
}
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
When I run this test, I got a MockitoException:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'userService'.
...
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
Instead of using the interface, I tried to use the real implementation; changing the test like this:
#InjectMocks
UserServiceImpl userService;
Now, the test passes with success, but this don't appear be right (at least for me).
I like to test the UserService that Spring Boot is using (suppose that in a new version of my system, I implement a new UserServicePostgreSQLImpl - now I'm using MongoDB).
(edit: see the bottom edit in the question)
I changed the Unit Test as follows:
#Autowired
#InjectMocks
UserService userService;
but now I got a test failure:
Expected :model.User#383caf89
Actual :null
For some reason, when I use #Autowired, the UserRepository mock doesn't work.
If I change the emailTest to use a real email in my database,
the test passes.
When I use #Autowired,
the test is using the real UserRepository and not a Mock version of UserRepository.
Any help?
Edit: looking at the answers from #msfoster and #dunni, and thinking better, I believe that the correct way is to test every implementation (in my example, use UserServiceImpl userService).
In order for your UserServiceImpl to be autowired when annotating it with #InjectMocks then it needs to registered as a Spring bean itself. You can do this most simply by annotating your UserServiceImpl class with #Service.
This will ensure it is picked up by the component scan in your Spring boot configuration. (As long as the scan includes the package your service class is in!)
You are running your tests with SpringRunner but for mocks you don't really need spring context. Try following code
// Using mockito runner
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
// Mockito will auto inject mockUserRepository mock to userService via constructor injection
#InjectMocks
UserService userService;
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
This is just a variation on the #Yogesh Badke answer.
Although you are using spring at runtime,
there is no need to use spring during the unit test.
Instead,
you can mock all the dependencies and set them to the mocks during test setup
(using reflection or setters, if you have them).
Here is some example code:
import org.springframework.test.util.ReflectionTestUtils;
public class TestUserService
{
private static final String VALUE_EMAIL = "test email value";
private UserService classToTest;
#Mock
private User mockUser;
#Mock
private UserRepository mockUserRepository;
#Before
public void beforeTest()
{
MockitoAnnotations.initMock(this);
classToTest = new UserService();
doReturn(mockUser).when(mockUserRepository).findByEmail(VALUE_EMAIL);
ReflectionTestUtils.setField(
classToTest,
"userRepository",
mockUserRepository);
}
#Test
public void findByEmail_goodEmailInput_returnsCorrectUser()
{
final User actualResult;
actualResult = classToTest.findByEmail(VALUE_EMAIL);
assertSame(
mockUser,
actualResult);
}
}
If interface is implemented by more than one class, then use the qualifier name (example below) in Junit beans.xml file to run the respective Junit test case.
Example:
#Autowired
#Qualifier("animal")
private Animal animals;
In Junit beans.xml
<bean id="animal" class="com.example.abc.Lion"/>
where Lion is the implementation class for the Interface Animal.
You need to #InjectMocks for the implementation class. Not the interface class.
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
#InjectMocks
UserServiceImpl userServiceImpl; ------> This is important
}
Repository object not injecting in testcase class.
Here is my below test class code
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classesExternalProviderMain.class)
#ActiveProfiles(ApplicationConstants.DEVELOPMENT_PROFILE)
#WebAppConfiguration
public class EmployeeServiceTest {
#InjectMocks
EmployeeService employeeService; //not injected null
#Mock
EmployeeRepository employeeRepository;//not injected null
#Test
public void testEmployee() {
Mockito.when(employeeRepository.findByName(Stringname)).thenReturn(getEmployee());
List<Employee> resultedTrackbles = employeeService.getEmployeeByName("mike");
}
private List<Employee> getEmployee(){
//here is the logic to return List<Employees>
}
}
Can you please help me how to inject my "EmployeeRepository" and Do need to write any additional logic.
That's because you are running your test with SpringJUnit4ClassRunner and not with MockitoJUnitRunner.
The mocks need to be initialized with MockitoAnnotations.initMocks:
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}