Mocking EntityManager (Mockito) - unit-testing

I have the following code in one of my classes:
#PersistenceContext
protected EntityManager entityManager;
public entryPoint() {
for(Animal:getTheAnimals()) {
System.out.println(Animal.getName());
}
}
private List<Animal> getTheAnimals() {
return List<Animal>entityManager.createNamedQuery("myQuery").setParameter("myParam", new Date()).getResultList();
}
In my test class I have the following:
#Mock
private EntityManager entityManager;
#Mock
private Query query;
#Autowired
private ClassToTest classToTest;
#Test
public void someTest() {
List<Animal> list = new ArrayList<Animal>();
Mockito.when(entityManager.createNamdeQuery("myQuery")).thenReturn(query);
Mockito.when(query.setParameter(any(String.class), any(java.util.Date.class)).getResultList()).thenReturn(list);
...something more here...
}
As you can see the expected behavior is that the empty list is returned, and zero animal names get printed. However that is not the case and the actual animals from the db are being returned in the list. What am I missing? I tried several variations of this with the same result.
Thanks for reading.

In your question, you use #Autowired directly on the field for the system under test, which seems to instruct Spring to resolve the dependencies. It likely does so from your actual (production) configuration.
By comparison, a common Mockito annotation is #InjectMocks, which ignores Spring, instantiates the object using as many #Mock objects as possible.
Related SO question: Injecting into #Autowired variable during testing

Related

cannot stub ReactiveMongoRepository using Mockito.when

I'm new to reactive programming. I want to write some test cases for a reactive mongo repository. I tried to stub some query methods and use step-verifier to check the response, but my test gets fail .
ItemReactiveRepository.java
public interface ItemReactiveRepository extends ReactiveMongoRepository<Item, String> {
Mono<Item> findByDescription(String description);
}
Item.java
#Document
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Item {
#Id
private String id;
private String description;
private Double price;
}
ItemReactiveRepositoryTest.java
#DataMongoTest
#RunWith(SpringRunner.class)
public class ItemReactiveRepositoryTest {
#Autowired
private ItemReactiveRepository itemReactiveRepository;
#Test
public void findById() {
Item itemForTest = new Item("ABC", "Samsung TV", 405.0);
Mockito.when(itemReactiveRepository.findById("ABC")).thenReturn(Mono.just(itemForTest));
StepVerifier.create(itemReactiveRepository.findById("ABC"))
.expectSubscription()
.expectNextMatches(item -> item.getPrice() == 405.0)
.verifyComplete();
}
}
Error I receive when running test
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
inside when() you don't call method on mock but on some other object.
Are there any limitations to use stubbing when test reactive streams? Or any other standard mechanism to test above scenarios?
Instead of using #Autowired you have to prepare mock for Repository from import org.mockito.Mock;
#Mock
ItemReactiveRepository itemReactiveRepository;
As #Thomas has mentioned, you are not mocking instead using the actual MongoDB using DataMongoTest instead you have to get rid of this and just mock the methods and test your service layer. Autowired is expecting all default configuration and a bean which is prepared by the container for you to use which is not the case, you have to mock and use.
ItemReactiveRepository itemReactiveRepository=org.mockito.Mockito.mock(ItemReactiveRepository.class);
This worked for me.

Spring boot Unit test with #WebMvcTest - execution doesn't enter service layer from controller and returns null without error

I am writing unit test for a controller of my spring boot application.
I have typical MVC classes: ObjectSchemaController, ObjectSchemaService and ObjectSchemaDao.
I have written unit test with #WebMvcTest and mocked my service and dao class with #MockBean. (following this guide: https://www.baeldung.com/spring-boot-testing)
Below is my unit test :
#RunWith(SpringRunner.class)
#WebMvcTest(ObjectSchemaController.class)
public class ObjectSchemaControllerTest2 {
#Autowired
private MockMvc mvc;
#MockBean
private ObjectSchemaService service;
#MockBean
private ObjectSchemaDao dao;
#Autowired
ObjectMapper objectMapper;
#Test
public void testCreateObjectSchemaPass() throws Exception {
String payload = "{\"some_key\":\"some val\"}";
ObjectSchema objectSchema = objectMapper.readValue(payload, ObjectSchema.class);
Mockito.when(service.createSchema(objectSchema))
.thenReturn(objectSchema);
Mockito.when(dao.createSchema(objectSchema)).thenReturn(objectSchema);
mvc.perform(MockMvcRequestBuilders.post("/objectservice/schema/")
.contentType("application/json")
.content(objectMapper.writeValueAsString(objectSchema)))
.andExpect(status().isOk());
}
}
below is my service class:
#Service
public class ObjectSchemaService {
#Autowired
ObjectSchemaDao objectSchemaDao;
public ObjectSchema createSchema(#Valid ObjectSchema objectSchema)throws Exception {
return objectSchemaDao.createSchema(objectSchema);
}
}
The issue I am facing with Unit test is, the service layer doesn't get executed and returns null value.
When I debug, I can see execution reaching in my controller class and ObjectSchemaService as being mockito-mocked in the controller. But the execution never goes in service layer and the value returned by service method is null.
I have referenced other guides- they are doing similar steps. But its not working for me. What am I missing here?
I have also seen this post with similar issue.
Unit Test POST with #WebMvcTest - #MockBean Service returns null
I made sure the input objects to both my actual controller and the one I am passing in unit case are instances of same class.
You are mocking the ObjectSchemaService but no behaviour is expected.
You need to setup the behaviour for the services that are mocked. So depending on the method signature and result somethink like.
Mockito.when(service.createSchema(Mockito.any(ObjectSchema.class)).thenReturn(objectSchema);
At the moment the ObjectSchemaService mock just returns a default value which is null in your case.
In order to be transparent and unobtrusive all Mockito mocks by default return 'nice' values. For example: zeros, falseys, empty collections or nulls.
If you update your answer with details for ObjectSchemaService I could also update my answer.
You mock ObjectSchemaService so you need to tell the service how mock the values from the service when a method is called. If you don't mock the values of the service Mockito don't know what they have to return always give you null. Not need to mock ObjectSchemaDao in this test.
Note: I use Lombok in the code as ObjectSchema.builder() to return the object with the Id when is stored in the database, you can use a constructor. Assuming the service return the object.
The code looks like this:
import static org.mockito.BDDMockito.given;
#Test
public void testCreateObjectSchemaPass() throws Exception {
String payload = "{\"some_key\":\"some val\"}";
ObjectSchema objectSchema = objectMapper.readValue(payload, ObjectSchema.class);
given(service.createSchema(objectSchema)).willReturn(
ObjectSchema.builder()
.id(1)
.someKey("Some Val")
.build());
mvc.perform(MockMvcRequestBuilders.post("/objectservice/schema/").contentType("application/json").content(objectMapper.writeValueAsString(objectSchema)))
.andExpect(status().isOk());
}

#WebMvcTest No qualifying bean of type repository

I'm writing a controller test where controller looks like
#RestController
public class VehicleController {
#Autowired
private VehicleService vehicleService = null;
...
}
While the test class looks like
#RunWith(SpringRunner.class)
#WebMvcTest(VehicleController.class)
public class VehicleControllerTest {
#Autowired
private MockMvc mockMvc = null;
#MockBean
private VehicleService vehicleServie = null;
#Test
public void test() {
...
}
}
When I run this test it fails with the following error
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.database.repositories.SomeOtherRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Here, SomeOtherRepository is not used in a given controller or service.
If I do #MockBean for SomeOtherRepository the test works but the same problem occurs for the rest of the repositories.
#MockBean private SomeOtherRepository someOtherRepository = null
...
# Bunch of other repositories
Ideally I should not be concerned about all the repositories except the one's I'm using. What am I missing here? How can I avoid writing bunch of #MockBeans?
You have specified
#WebMvcTest(VehicleController.class)
which is fine, however you might find some beans from other dependencies, such as a custom UserDetailsService, some custom validation, or #ControllerAdvice that are also being brought in.
You can exclude these beans by using exclude filters.
#WebMvcTest(controllers = VehicleController.class, excludeFilters = #Filter(type = FilterType.ASSIGNABLE_TYPE, classes = CustomUserDetailsService.class)
Your VehicleService depends on SomeOtherRepository, so you have to mock it too in your test class. Try adding:
#MockBean private SomeOtherRepository someOtherRepository
in your VehicleControllerTest class.

Mocking field in #Mock class

Since I am new to Mockito, I would like to know, how can I mock a method inside a class, which actually is also annotated with #Mock.
Example:
#RunWith(MockitoJUnitRunner.class)
public class someServiceTest {
#InjectMocks
private MainService mainService;
#Mock
private HelpService helpService;
#Mock
private SecondHelpService secondHelpService;
Now there is this helpService class, which contains a method, which is used to test MainService.
#Service
#Transactional(propagation = Propagation.SUPPORTS)
public class HelpService {
// I want this method to be mocked
private boolean checkSomething(String name, date) {
return ProcessService.checkIfJobHasRun(name, date);
}
I tried to use #InjectMocks on HelpService as well, by adding ProcessService #Mock in someServiceTest, but Mockito doesn't allow that. And as a result, it throws me NullPointerException. How do I fix that?
#InjectMocks should only be used on the class under test. It does not make sense to inject dependencies into mocks as they do not have any implementation.
If you can make the method public you could mock it as follows:
doReturn(true).when(helpService)
.checkSomething(Mockito.any(String.class), Mockito.any(Date.class));

Change application property value in unit test method

My REST API generates a password on a POST request and stores it in a DB. For development purpose, I'd like to put it in the response, when a development.mode flag is set to true in the application.properties file.
I would like to test this controller and check that if the flag is true, the password is in the response, if false the password is not.
I would like to know if and how I can dynamically set this property value from each test method.
This is my controller:
#RestController
public class PasswordController {
#Value("${development.mode}")
private boolean isDevelopmentMode;
#RequestMapping(value = "/new", method = RequestMethod.POST)
public ResponseEntity<String> generatePassword(#RequestParam(USERNAME_PARAM) String username) {
String password = manager.generatePassword();
String body = "";
if (isDevelopmentMode) {
body = "New password: " + password;
}
return new ResponseEntity<>(body, HttpStatus.OK);
}
}
I have seen documentation to define different property files for testing, using #PropertySource, but it's at the class level and I would like to avoid creating a different file just to change a flag.
I've also seen posts saying you can use ReflectionUtils.setField to change the value of a private field, but for this I have to explicity instantiate my controller and consequently have some issues with Autowired fields. I've tried this:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordControllerTest.class)
public class PasswordControllerTest extends OtpTest {
#Autowired
private MockMvc mockMvc;
#Test
public void newRoute_developmentModeIsFalse_responseBodyIsEmpty() throws Exception {
PasswordController controller = new PasswordController();
ReflectionTestUtils.setField(controller, "isDevelopmentMode", false);
MockMvc mvc = MockMvcBuilders.standaloneSetup(new PasswordController()).build();
given(manager.generatePassword(any(String.class))).willReturn("123456");
mvc.perform(post("/new").param(PasswordController.USERNAME_PARAM, "test_user")).andExpect(status().isOk()).andExpect(content().string(""));
}
}
Is there a possibility to set the value of a #Value field in each test method? Should I refactor my code to access it differently?
SOLUTION
Thanks to Borys Zibrov it works with this code:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordController.class)
public class PasswordControllerTest extends OtpTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext applicationContext;
#Test
public void newRoute_developmentModeIsTrue_passwordIsInTheResponseBody() throws Exception {
PasswordController controller = applicationContext.getBean(PasswordController.class);
ReflectionTestUtils.setField(controller, "isDevelopmentMode", true);
given(manager.generatePassword(any(String.class))).willReturn("123456");
mockMvc.perform(post("/new").param(PasswordController.USERNAME_PARAM, "test_user")).andExpect(status().isOk())
.andExpect(content().string("New password: 123456"));
}
}
Getting my controller bean from the application context didn't work at first because in my previous code I was using my test class in WebMvcTest annotation.
You could create another applicationContext-tests.xml, connect it to your test via:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordControllerTest.class)
#ContextHierarchy({
#ContextConfiguration(locations = "path_to applicationContext-tests.xml")
})
public class PasswordControllerTest extends OtpTest {
and override that property development.mode there.
You could also stick to using ReflectionTestUtils but instead of a direct instantiation use a bean from applicationContext constructed by spring for you, and then set a property on that bean.
Or you could just provide a setter for that field.