mapper in unit test is return null - java

I have service method which return mapper to convert entity to DTO when I run the application everything work successfully but when I do unit test the mapper return null.
Also I should mention that, this service is being called by another service "customerDetails" which is under the test.
code snippet, I put comments to describe the problem more :
customerService
public class customerService {
private final CustomerMapper customerMapper;
public Customer customerDetails(int id) {
CustomerDto customer = getById(id) //here is the problem customer is null
// rest of the code
}
public CustomerDto getById(int id) {
Optional<Customer> customer =
this.customerRepository.findCustomerByIdAndIsDeletedFalse(id); //assessment is filled successfully
return this.customerMapper.map(customer.get()); //the mapper her return customerDto and accept customer and it return null in unit test only
}
}
customerServiceTest
public class CustomerServiceTest {
#Mock
private CustomerRepository customerRepository;
#InjectMocks
private CustomerService customerService;
#BeforeEach
public void createMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCustomerDetails() {
Customer expectedResponse = DummyCustomer.create();
when(customerRepository.findCustomerByIdAndIsDeletedFalse(actualResponse.getId()).thenReturn(Optional.of(expectedResponse));
Customer response = this.CustomerService.customerDetails(expectedResponse.getId());
}
}

In actual code Spring handles injection of your mapper for you - but in unit test you don't have spring context set up. In fact you'd have seen the issue earlier if instead of relying on #InjectMocks you tried to initialize the service manually.
As to solutions - in test code you can get an instance of your mapper using org.mapstruct.factory.Mappers.getMapper() method. Use it and set it in your service under test properly (however you inject your dependencies - via constructor or setter). Or, if you want a "pure" unit test of just one component, mock it.

Related

Junit Mocked bean is not executing

I'm using spring boot rest API and want to test my service layer. In my service I have autowired few beans and its not through constructor. (I like it that way to keep it short).
In the junit, I have created mocks and for private field which I do want to execute, I have assigned using ReflectionTestUtils.setField. When I debug, the method inside the field is not getting executed which assigned by bean.
Service Class
#Component
public class MyService {
#Autowired
private MyRepository myRepository;
#Autowired
private ResponseMapper responseMapper;
public List<MyObj> getList(String param) throws MyException {
log.info("Getting details");
Optional<List<MyObj>> list = myRepository.findByParam(param);
List<MyObj> data = responseMapper.mapToResponseData(list);
return data;
}
}
Test Class
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private MyRepository myRepository;
#Mock
private ResponseMapper responseMapper;
#Before
public void setUp() {
ReflectionTestUtils.setField(myService, "responseMapper", responseMapper);
}
#Test
public void getListTest() throws Exception {
when(myRepository.findByParam(anyString()))
.thenReturn(Optional.of(getSampleList()));
List<MyObj> list = myService.getList("param");
assertTrue(list.size() >0);
}
}
This results in Assertion failure and when I debug, the method mapToResponseData in ResponseMapper is not getting executed.
I know I can mock mapToResponseData method also. But I wanted it to execute so I don't have to write another test class for mapper alone.
What am I doing wrong? Is this the right wat to inject bean? And is using constructor in service to inject beans only option?

Nullpointer when using Mockito for unit tests

I am creating a spring boot API for my application. I am attempting to unit test my service implementation using mockito to mock out the detail. The service will add a new building entity to the database. Below is the Service implementation and the test implementation.
Building Service:
#Slf4j
#Service
public class BuildingServiceImpl implements BuildingService {
private BuildingRepository buildingRepository;
private BuildingRequestToEntityMapper buildingRequestToEntityMapper;
public BuildingServiceImpl(BuildingRepository buildingRepository, BuildingRequestToEntityMapper
buildingRequestToEntityMapper){
this.buildingRepository=buildingRepository;
this.buildingRequestToEntityMapper=buildingRequestToEntityMapper;
}
public HttpStatus addBuilding(BuildingRequest buildingRequest){
log.info("Inside addBuilding() service");
BuildingEntity buildingEntity = buildingRequestToEntityMapper.map(buildingRequest);
buildingRepository.save(buildingEntity);
log.info("Building saved "+ buildingEntity);
return HttpStatus.CREATED;
}
BuildingServiceImpl_UT
#RunWith(MockitoJUnitRunner.class)
public class BuildingServiceImpl_UT {
#Mock
BuildingRequestToEntityMapper buildingRequestToEntityMapper;
#Mock
BuildingRepository buildingRepository;
#InjectMocks
BuildingServiceImpl buildingServiceImpl;
#Test
public void buildingService_MapsRequest_AndSaveEntity(){
BuildingRequest buildingRequest = BuildingRequest.builder()
.name("TestName")
.telephone("4444444444")
.postcode("TEst")
.address("testAddress").build();
when(buildingServiceImpl.addBuilding(any(BuildingRequest.class))).thenReturn(HttpStatus.CREATED);
when(buildingRepository.save(any(BuildingEntity.class))).thenReturn(new BuildingEntity());
buildingServiceImpl.addBuilding(buildingRequest);
verify(buildingRepository, times(1)).save(any());
}
I have mocked the mapper and repository and injected them into the service, but when i run the test I get a null pointer exception at the first when().thenReturn() statement in the test class. Any help please. Thanks
I don't understand your first when().thenReturn()! You try to do this on the buildingServiceImpl wich is not a mock! Further more this makes no sense because you want to test this methode!
I think you should define a when().thenReturn() for the mock buildingRequestToEntityMapper, but in your implementation you don't need to define a return for buildingRequestToEntityMapper.map(). In this case the variable buildingEntity will have the value null which should work in your test case.
#RunWith(MockitoJUnitRunner.class)
public class BuildingServiceImpl_UT {
#Mock
BuildingRequestToEntityMapper buildingRequestToEntityMapper;
#Mock
BuildingRepository buildingRepository;
#InjectMocks
BuildingServiceImpl buildingServiceImpl;
#Test
public void buildingService_MapsRequest_AndSaveEntity(){
BuildingRequest buildingRequest = BuildingRequest.builder()
.name("TestName")
.telephone("4444444444")
.postcode("TEst")
.address("testAddress").build();
when(buildingRepository.save(any(BuildingEntity.class))).thenReturn(new BuildingEntity());
buildingServiceImpl.addBuilding(buildingRequest);
verify(buildingRepository, times(1)).save(any());
verify(buildingRequestToEntityMapper).map(any());
}
This:
when(buildingServiceImpl.addBuilding(any(BuildingRequest.class))).thenReturn(HttpStatus.CREATED);
is not necessary, you want to test method: addBuilding not mock it.

Mockito when isn't replacing original method behaviour

I got 2 modules User and Email, both of them have 1 entry point which is a facade, rest is package scoped. The configuration is done in 2 classes
#Configuration
class UserConfiguration {
#Bean
UserFacade userFacade(UserRepository repository, EmailFacade emailFacade) {
return new UserFacade(repository, emailFacade);
}
}
#Configuration
class EmailConfiguration {
#Bean
EmailFacade emailFacade(EmailSender emailSender) {
return new EmailFacade(emailSender);
}
}
Now, I want to write tests that don't require Spring to start. I implemented a simple InMemoryRepository to make this happen
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade = new EmailFacade(new FakeEmailSender());
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I need some fake objects to instantiate EmailFacade so I wrote fake implementation
public class FakeEmailSender implements EmailSender {
#Override
public void sendEmail(EmailMessage emailMessage) throws RuntimeException {
}
}
In that scenario, I'm testing User domain, so I want to mock Email anyways.
I wrote a test to check if it works
#Test
public void shouldReturnSendingFailed() {
Mockito.when(emailFacade.sendUserVerificationEmail(Mockito.any())).thenReturn(Either.left(EmailError.SENDING_FAILED));
assertThat(userFacade.registerNewUser(RegisterUserDto.builder()
.username(USERNAME_4)
.email(VALID_EMAIL)
.password(VALID_PASSWORD).build()).getLeft(), is(EmailError.SENDING_FAILED));
}
But it isn't... after running this test I got
java.util.NoSuchElementException: getLeft() on Right
edit#
regiserNewUser() method
Either<DomainError, SuccessMessage> register(RegisterUserDto registerUserDto) {
if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
return Either.left(UserError.USERNAME_ALREADY_EXISTS);
var userCreationResult = User.createUser(registerUserDto);
var savedUser = userCreationResult.map(this::saveUser);
var emailDto = savedUser.map(this::createVerificationEmail);
return emailDto.isRight() ? emailFacade.sendUserVerificationEmail(emailDto.get())
: Either.left(emailDto.getLeft());
}
Edit2#
With following test configuration
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I got nullpointer here, last line of registerNewUser().
Try running this code
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
private UserFacade userFacade;
#Before
public void setUp() {
userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
}
}
There are a few issues with your code:
You initialize your mocks twice. You don’t need to call initMocks in the setUp method if you are using Mockito runner
You are trying to inject mocks to already initialized object. But the field you are trying to inject is also passed to the constructor. Please read #InjectMocks doc, to check the strategies used to inject the mocks:
constructor (not used here, already initialized object)
setter (do you have one?)
field (is it not final)
There are details to each strategy (see my questions above). If no staregy is matched, Mockito will fail silently. The fact that you are passing an object in constructor, and rely on setter or field injection afterwards makes this code unnecesarily complex.

Test a service in Spring with a Mock, What I'm doing wrong?

I have a Spring-boot project, in witch I have controller, service and mapper layer. Now I want to test a service and I want to mock the mapper. I do it in this way:
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class)
#Transactional
public class SomeServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
#Mock
private AMapper aMapper;
#Autowired
#InjectMocks
AService aService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
executeSqlScript("classpath:insertSomeData.sql", false);
}
#Test
public void testMethod() throws Exception {
//prepareSomeData
aService.callMethod(someData);
verify(aMapper).callTheRightMethod(rightObject);
}
And the service:
#Service
#Transactional(readOnly = true)
public class AServiceImpl implements AService {
#Autowired
BMapper bMapper;
#Autowired
CMapper cMapper;
#Override
#Transactional(readOnly = false)
public SomeReturnObject callMethod(SomeData someData)throws Exception {
//some execution to obtain aResult
if(true){
aMapper.callTheRightMethod(aResult);}
else
aMapper.callWrongMethod(aResult);
}
Now when I execute the test the result is:
Wanted but not invoked:
aMapper.callTheRightMethod{..}
Actually, there were zero interactions with this mock.
When i debug then I see that the method is called, but probably it's the wrong mapper (not the mocked). Have you some tips to figure out that issue?
I can't see the mock interaction recording here. It should come before the actual invocation. It should be something like this.
Mockito.when(aMapper.callTheRightMethod(Mockito.any()).thenReturn(rightObject);
The flow should be like this. Firstly record the mocks, then perform actual invocation and finally verify the mock interactions. As above #Autowire is not needed for the test class. Please remove that too. Instead create a new instance of service class by passing some data through it's constructor. Hope this helps. Happy coding !
I don't exactly understand why would you start up spring context for testing just a service layer. Test only one layer at a time.
That's how I would address the problem. (If something does not compile, my apologies..writing from top of my head)
#RunWith(MockitoJUnit4ClassRunner.class)
public class SomeServiceTest {
#Mock
private AMapper aMapper;
#InjectMocks
AService aService = new AService();
#Test
public void testMethod() throws Exception {
// given
Mockito.doReturn(aResult).when(aMapper).getAResult();
// when
aService.callMethod(someData);
// then
verify(aMapper).callTheRightMethod(rightObject);
}

Spring Boot Error When Running Unit Tests

After almost 8 years, I have to dust my Spring knowledge and things were good as long as I did not have to write unit tests. I have the following unit test that would test one of my service and when I tried to run it, it would fail with an:
org.springframework.beans.factory.UnsatisfiedDependencyException
and it is not able to resolve the eMailNotificationService service!
So here is my Unit test:
#ActiveProfiles("test")
#ComponentScan(basePackages = {"com.middleware.service.email", "it.ozimov.springboot.templating.mail"})
#RunWith(SpringRunner.class)
public class EMailNotificationServiceTest {
#Autowired()
private EMailNotificationService eMailNotificationService;
#MockBean(name = "emailService")
private EmailService emailService;
#Test
public void sendResetPasswordEMailNotification() {
System.out.println(eMailNotificationService);
// TODO: complete the test
}
}
The EMailNotificationService is as below and this is defined in the com.middleware.service.email package:
#Service()
#Scope("singleton")
public class EMailNotificationService {
private static Log logger = LogFactory.getLog(EMailNotificationService.class);
#Value("${service.email.sender}")
String senderEMail;
#Value("${service.email.sender.name}")
String senderName;
#Value("${service.email.resetPassword.link}")
String resetPasswordLink;
#Autowired
public EmailService emailService;
public void sendResetPasswordEMail(List<EMailUser> userList) {
List<String> allEMails = userList.stream()
.map(EMailUser::getUserEMail)
.collect(Collectors.toList());
userList.stream().forEach(emailUser -> {
final Email email;
try {
email = DefaultEmail.builder()
.from(new InternetAddress(senderEMail, senderName))
.to(Lists.newArrayList(new InternetAddress(emailUser.getUserEMail(), emailUser.getUserName())))
.subject("Reset Password")
.body("")//Empty body
.encoding(String.valueOf(Charset.forName("UTF-8"))).build();
// Defining the model object for the given Freemarker template
final Map<String, Object> modelObject = new HashMap<>();
modelObject.put("name", emailUser.getUserName());
modelObject.put("link", resetPasswordLink);
emailService.send(email, "resetPasswordEMailTemplate.ftl", modelObject);
} catch (UnsupportedEncodingException | CannotSendEmailException ex) {
logger.error("error when sending reset password EMail to users " + allEMails, ex);
}
});
}
}
How do I write my unit test such that my service is injected / autowired?
I prefer use this constructions:
public static void manuallyInject(String fieldName, Object instance, Object targetObject)
throws NoSuchFieldException, IllegalAccessException {
Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, targetObject);
}
in your case : manuallyInject("eMailNotificationService", emailService, eMailNotificationService)
You have used in your test a #MockBean annotation which comes with a spring boot test. Hence, if you are relying on features that spring-boot-test provides, you have to mark your test with a #SpringBootTest annotation (in which case you may also remove a #ComponentScan annotation). Consequently, spring will properly mock your dependencies (EmailService) and will provide you with EmailNotificationService bean.
Further information regarding spring boot testing that may be useful can be found in their reference documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
Update:
Such tests bring up the whole context which may be unnecessary and fit rather to integration tests rather than unit tests. For 'pure' unit tests, you can forget that you are testing spring services and treat your classes as POJO. In that case, you can use Mockito for mocking your dependencies (which btw comes with spring-boot-test). You can rewrite your code in the following manner:
#RunWith(MockitoJUnitRunner.class)
public class EMailNotificationServiceTest {
#InjectMocks
private EmailNotService eMailNotificationService;
#Mock
private EmailService emailService;
#Before
public void setup() {
eMailNotificationService.setSenderEMail("myemail");
// set other #Value fields
}
#Test
public void sendResetPasswordEMailNotification() {
System.out.println(eMailNotificationService);
// TODO: complete the test
}
}

Categories

Resources