Null is passed to autowired service even after mocking - SpringBootTest - spring-boot

I wanted to do integration testing of my API.
#RestController
#RequestMapping("/api/v1")
public class TestController {
#Autowired
TestService testService;
#RequestMapping("/welcome")
public String welcomeMessage(#RequestParam("name") String name) {
return testService.welcomeMessage(name);
}
}
Below are the service interface and its implementation:
public interface TestService {
public String welcomeMessage(String name);
}
public class TestServiceImpl implements TestService{
#Autowired
TestRepository repo;
#Override
public String welcomeMessage(String name) {
repo.save(new StringEntity(name));
return "Hello "+name;
}
}
Below is the Test Case:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MockitoTestingApplicationTests {
#Autowired
MockMvc mvc;
#MockBean
TestService testService;
#MockBean
TestController testController;
#Test
public void contextLoads() throws Exception {
Mockito.when(testController.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();
Mockito.when(testService.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();
mvc.perform(get("/api/v1/welcome").param("name", "dude")).andExpect(status().isOk());
}
}
I have a few questions.
when I'm executing the above code it is throwing an error saying cannot call real method on abstract methods. And When I'm mocking the TestServiceImpl, It is throwing NullPointerException in the Controller because the TestService is null. How should I fix that?
How should I mock the repository layer when we are using MongoDB. when I try to Mock MongoTemplate, It is throwing an error saying MongoConvertor must not be null
Is this the right way to write test cases. can we have code coverage without using thenCallRealMethod()?
Please suggest me how to proceed. Thanks in advance.

Make sure you have an implementation of the service i.e. TestServiceImpl annotated with #Service (or #Component if it is not strictly a service) and use spying instead of mocking:
#SpyBean
TestService testService;
Spying by default call real methods so you have to mock these that implementation you do not want to call.
Regarding repositories, you should mock the components annotated with #Repository, not the actual SessionFactory / Template etc. that are used within.

Related

How to mock a method in Service layer

While doing unit tests on each method of the service layer, I have encountered the below scenario, that I couldn’t figure out how to test:
public class UserServiceImpl{
#Autowired
UserRepository userRepository;
public void abc(){
xyz(obj);
}
private void xyz(){
userRepository.save(obj);
}
}
What I want to test is the abc() method. Within that method, it invokes xyz() which is a PRIVATE method that uses the userRepository dependency. So, when I create a unit test for the abc() method, do I need to be concerned about the xyz() method since it is using a dependency? And if yes, what are the steps that I need to follow?
As you wrote you need to deal with xyz() method and its call to userRepository. You need to mock userRepository as follows:
#ExtendWith(MockitoExtension.class)
public class UserServiceImplTest {
#Mock
private UserRepository userRepository;
#InjectMocks
public UserServiceImpl userService;
#BeforeEach
public void setUp() throws Exception {
// Mock UserRepository behaviour
doReturn(//return value).when(this.userRepository).save(any());
}
// Your tests here
}
Since this is a void method, what you want to do is verify that the save method of the dependency has been called exactly once with the parameter obj. You could do this by using something like Mockito. Your unit test would look something like this:
#Mock
private UserRepository mockUserRepository;
#InjectMocks
private UserServiceImpl sut;
#Test
public void abc_savesObject() {
// Arrange
...
// Act
sut.abc();
// Assert
verify(mockUserRepository,times(1)).save(obj);
}
Some useful links:
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#4
https://www.baeldung.com/mockito-verify

NPE when not injecting mock data for mapper class (spring boot)

I am using spring boot. I have the below mapper class to map the entity object to model object.
#Mapper(componentModel = "spring")
public interface PersonMapper {
PersonEntity personModelToPersonEntity(PersonModel pm)
PersonModel personEntityToPersonModel(PersonEntity pe)
}
Service class:
#Service
public class MyService{
#Autowired
PersonMapper personMapper;
#Autowired
PersonRepository personRepo;
#Transactional
public List<Person> getDetails() {
List<Person> personList = personRepo.findAll();
List<PersonModel> pm = personMapper.personEntityToPersonModel(personList);
...
...
}
}
I have the below Junit test case, where I'm mocking the data.
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
// #Mock
// PersonMapper personMapper;
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
given(personRepo.findAll()).willReturn(mymockData);
//given(personMapper.personEntityToPersonModel(..).willReturn(..);
...
}
}
Eveything works if I mock the personMapper, but i don't want to mock the mapper class, when test case is executing, as i'm sending the mock entity data using "given(personRepo.findAll()).willReturn(mymockData);" so when it hits the service it should automatically convert the mock data sent to model object. In my case when i have commented the code for mocking mapper class, it is throwing NullPointerException in service class at the mapper object.
As #M. Denium said, nothing is there, because application context isn't raising with MockitoExtension. So, you can either mock your PersonMapper and stub its behavior or inject it manually with ReflectionTestUtils:
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
PersonMapper personMapper = Mappers.getMapper(PersonMapper.class);
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
ReflectionTestUtils.setField(personService, "personMapper", personMapper);
given(personRepo.findAll()).willReturn(mymockData);
...
}
}

Testing Conditionally Initialized RestController based on active profile

I have the following class, which is loaded conditionally based on the profile. I want it to only load for assisting in lower environments and not production:
#RestController
#RequestMapping("/foo")
#Profile("!prod")
public class FooController {
private Bar bar;
#PostMapping
public ResponseEntity<?> createFoo(){
bar.something();
return new ResponseEntity<Object>(HttpStatus.ACCEPTED);
}
}
I would like to have the following tests for this class:
#WebMvcTest
#RunWith(SpringRunner.class)
#ActiveProfiles("prod")
public class FooControllerTest {
private MockMvc mockMvc;
#Mock
private Bar bar;
#InjectMocks
private FooController subjectUnderTest;
#Before
public void init(){
mockMvc = MockMvcBuilders
.standaloneSetup(subjectUnderTest)
.build();
}
#Test
public void givenProdProfile_whenCreateFoo_thenNotFoundResponseReturned() throws Exception {
doNothing().when(mockBar).something();
mockMvc.perform(MockMvcRequestBuilders
.post("/foo"))
.andExpect(status().isNotFound());
}
and then another test class to test this case, with #ActiveProfile('non-prod'):
givenNonProdProfile_whenCreateFoo_thenAcceptedResponseReturned
I am confused on how to properly test this. I've tried many different different approaches, and none of them seem to properly test the class. I've tried annotating my test class with #ActiveProfile, etc.
I suppose the crux of my question, is how can I test different profiles with MockMvc and Mockito?

How to Pass #Value to a #Component for Spring Unit Testing

Im writing unit tests for services, controllers, etc however theres is a #Component that has the following values
#Component
Public class myclass
#Autowired
Private MyTemplate myTemplate
#Value("$someString")
Private String someString
#PostConstruct
Public void loadString()
...
How would I manually load values into the #Values? I have tried with Mocks, TestPropertySource, ReflectionTestUtils, among other ways found around
You can inject the #Value in test class by ReflectionTestUtils. Load container only in case of Controllers. For writing test cases for services and dao you don't need to load the spring container.
public class TestClass{
private #InjectsMock ServiceClass service;
#BeforeAll
public void setUp(){
ReflectionTestUtils.setField(service, "someString", "someValue");
}
//your test cases over here.
}
I can immediately think of two options
1) You could define $someString in your test/resources/test.properties
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:test.properties")
public class ClassTest {
}
2) do it manually
#RunWith(SpringRunner.class)
public class ClassTest {
#Autowired
private MyClass miclass;
#Before
public void setupObject() {
miclass.setProperty("someting");
}
}

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.

Resources