How to mock mapstruct mapper output in a Spring Service? - java

I have a spring service (MyService) that uses a mapstruct mapper (CustomMapstructMapper) :
#Service
public class MyService {
private final ClassA classA;
private final ClassB classB;
/*
other private fields declarations...
*/
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
//MyService constructor
public MyService(final ClassA classA, etc...) {
this.classA = classA;
//etc...
}
public ServiceOutput mainMethod(some parameters) {
//some business logic
MyMapperOutput myMapperOutput = customMapstructMapper.map(MapperParameter parameter);
ServiceOutput serviceOutput = some business logic with myMapperOutput;
return serviceOutput;
}
}
I want to unit test MyService (I am using Junit 5) and mock my CustomMapstructMapper output without calling the real mapper during the test execution. I already have another test class that specificly test all the custom mappings in CustomMapstructMapper.
So I have this test class :
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private CustomMapstructMapper customMapstructMapper;
#Mock
private MyMapperOutput myMapperOutput;
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}
}
When I run my test, the implementation of my mapper customMapstructMapperImpl is called in MyService, not my mock.
A NullPointerException is thrown in my mapper because some fields are not initiated.
I have tried to create a mock with the implementation of my mapstruct mapper :
#Mock private CustomMapstructMapperImpl customMapstructMapper;
but I get the same result.
What am I doing wrong?

You're not taking advantage of the spring framework and mapstruct support for it.
in your service change:
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
into
#Autowired
private CustomMapstructMapper customMapstructMapper;
If you don't have it yet at your mapper use
#Mapper( componentModel = "spring" )
This will cause the generated mapper to have the #Component annotation from the spring framework, and it becomes possible to auto-wire this.
After making these changes your method of supplying a mock for testing should work.

The code below works correctly. So maybe something else happened. Your code example is not quite complete.
#ExtendWith(MockitoExtension.class)
public class TestForService {
#InjectMocks
private Service service;
#Mock
private Mapper mapper;
#Test
public void test_service() {
System.out.println("test_service start");
Mockito.when(mapper.doSomeTh()).thenReturn("mock mapper doSomeTh");
String result = service.service();
System.out.println("test_service end, result: " + result);
}
static class Service {
private Mapper mapper = Mapper.getMapper();
public String service() {
System.out.println("Service.service");
return mapper.doSomeTh();
}
public void setMapper(Mapper mapper) {
this.mapper = mapper;
System.out.println("Service.setMapper: " + mapper);
}
}
static class Mapper {
public String doSomeTh() {
System.out.println("Mapper.doSomeTh");
return "mapper end";
}
public static Mapper getMapper() {
System.out.println("Mapper.getMapper");
return null;
}
}
}
result:
Mapper.getMapper
Service.setMapper: mapper
test_service start
Service.service
test_service end, result: mock mapper doSomeTh

A simpler way here:
Just add a package-private setter method into your MyService
// for testing only
void CustomMapstructMapper setCustomMapstructMapper(CustomMapstructMapper mapper) {
this.customMapstructMapper = mapper;
}
and inject your mocked CustomMapstructMapper in test code
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
myService.setCustomMapstructMapper(customMapstructMapper); // inject your mocked customMapstructMapper here
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}

Related

Mocking of component with InjectMocks is not working for my test class

I have a spring boot app with autowired components. It works fine operationally, but mocking is not initializing the components properly. Here are the main components:
base processor:
#Component
public class Processor {
public String getType() { return null; }
public void transformInput(JsonObject data) { }
public void buildOutputRequest(String syncType) { }
}
, one of the 4 subtypes:
#Component
public class ProcessorType1 extends Processor {
#Override
public String getType() { return Type.TYPE1.getValue();}
public void transformInput(JsonObject data) {
// do dtuff
}
}
, supporting enum:
public enum Type {
TYPE1("TYPE1"),TYPE2("TYPE2"), TYPE3("TYPE3"), TYPE4("TYPE4");
private final String value;
public static final Map<Type, String> enumMap = new EnumMap<Type, String>(
Type.class);
static {
for (Type e : Type.values())
enumMap.put(e, e.getValue());
}
Type(String value) { this.value = value;}
public String getValue() { return value; }
}
,and the factory class:
#Component
public class ProcessorFactory {
#Autowired
public ProcessorFactory(List<Processor> processors) {
for (Processor processor : processors) {
processorCache.put(processor.getType(), processor);
}
}
private static final Map<String, Processor> processorCache = new HashMap<String, Processor>();
public static Processor getProcessor(String type) {
Processor service = processorCache.get(type);
if(service == null) throw new RuntimeException("Unknown type: " + type);
return service;
}
}
, then operationally, a calling service uses the factory similar to this:
#Service
MyService {
public processData (
// do stuff to get processor type and data
Processor processor = ProcessorFactory.getProcessor(type);
processor.transformInput(data);
)
}
Again, operationally this works fine. However, I attempt to mock the factory and its initialization like the following:
#RunWith(SpringJUnit4ClassRunner.class)
public class ProcessorFacTest
{
#InjectMocks
ProcessorFactory factory;
#Test
void testGetSyncProcessorDocument() {
String type = Type.TYPE1.getValue();
Processor processor = ProcessorFactory.getProcessor(type);
Assert.assertTrue(processor instanceof ProcessorType1);
}
}
My expectation was that since I am using #InjectMocks, and since ProcessorFactory has its constructor autowired, the constructor would be called by InjectMocks as part of the initialization. However, this is not happening. The processorCache is zero-length because the constructor is never called.
I realize I am mixing injection with static usages, but since it worked operationally, and since my understanding was that InjectMocks would handle the creation of the processorCache, that it should have worked for the test class as well, and its not.
I would be grateful for any ideas as to what I am doing wrong. Thank you
You never define any mocks that could be injected. Which dependencies should be autowired if you don't define any? The constructor is still called, but with an empty list of dependencies (use your debugger to set a breakpoint there and check for yourself).
It's also critical to note that mocks will return null, empty lists or 0/false by default for all methods, unless the mock has been set up properly.
You must define the mocks so that they can be picked up by #InjectMocks plus use the correct JUnit runner/extension:
#ExtendWith(MockitoExtension.class) // JUnit 5
// #RunWith(MockitoJUnitRunner.class) // JUnit 4
public class ProcessorFacTest
{
#Mock
ProcessorType1 processor1;
#Mock
ProcessorType2 processor2;
// ...
#InjectMocks
ProcessorFactory factory;
#BeforeEach // JUnit 5
// #Before // JUnit 4
void setup() {
Mockito.when(processor1.getType()).thenReturn(Type.TYPE1);
Mockito.when(processor2.getType()).thenReturn(Type.TYPE2);
}
#Test
void testGetSyncProcessorDocument() {
String type = Type.TYPE1.getValue();
Processor processor = ProcessorFactory.getProcessor(type);
Assert.assertTrue(processor instanceof ProcessorType1);
}
}
EDIT: the above won't work because the processors are not a direct dependency for your class under test, but are elements of a list. Mockito will not be able to inject the correct mocks.
And here's how you would write the test as a Spring test with mock beans:
#RunWith(SpringJUnit4ClassRunner.class)
public class ProcessorFacTest
{
#MockBean
ProcessorType1 processor1;
#MockBean
ProcessorType2 processor2;
// ...
#Autowired
ProcessorFactory factory;
#BeforeEach // JUnit 5
// #Before // JUnit 4
void setup() {
Mockito.when(processor1.getType()).thenReturn(Type.TYPE1.getValue());
Mockito.when(processor2.getType()).thenReturn(Type.TYPE2.getValue());
}
#Test
void testGetSyncProcessorDocument() {
String type = Type.TYPE1.getValue();
Processor processor = ProcessorFactory.getProcessor(type);
Assert.assertTrue(processor instanceof ProcessorType1);
}
}
But actually you don't need to use any annotations here, you could wire the dependencies manually just as simple:
public class ProcessorFacTest
{
ProcessorFactory factory;
#BeforeEach // JUnit 5
// #Before // JUnit 4
void setup() {
final ProcessorType1 processor1 = Mockito.mock(ProcessorType1.class);
final ProcessorType1 processor2 = Mockito.mock(ProcessorType2.class);
Mockito.when(processor1.getType()).thenReturn(Type.TYPE1);
Mockito.when(processor2.getType()).thenReturn(Type.TYPE2);
factory = new ProcessorFactory(
List.of(
processor1,
processor2,
// ...
));
}
#Test
void testGetSyncProcessorDocument() {
String type = Type.TYPE1.getValue();
Processor processor = ProcessorFactory.getProcessor(type);
Assert.assertTrue(processor instanceof ProcessorType1);
}
}
If your processors are simply, easy to instantiate and don't depend on other classes, it might even be simpler (and better, YMMV) to simply use the real collaborators instead of mocks.
And it might be easier to create a test implementation of your processor type, which can be easier to set up than a mock object created dynamically with Mockito. The code would look like this:
#BeforeEach // JUnit 5
// #Before // JUnit 4
void setup() {
factory = new ProcessorFactory(
List.of(
new TestProcessor(Type.TYPE1),
new TestProcessor(Type.TYPE2),
// ...
));
}
private static class TestProcessor extends Processor {
private final Type type;
public TestProcessor(final Type type) { this.type = type; }
public String getType() { return type.getValue(); }
// Base class methods do nothing already. If they were, you would override them here with an empty implementation:
#Override
public void transformInput(JsonObject data) { /* do nothing */ }
#Override
public void buildOutputRequest(String syncType) { /* do nothing */ }
}
NB. It's bad practice to use names that are already defined in the JDK. Type is one such name (java.lang.reflect.Type).
It might also be worthwhile to read Why is my class not calling my mocked methods in unit test? to get a good overview of common gotchas when setting up mocks.

Retryable annotation - Junit5 - Mockito - is it possible

Is it possible to write unit test using Junit 5 mockito for retryable annotations?
I am having a service interface which has only one method, which downloads the file from remote url
#service
interface downloadpdf{
#Retryable(value = { FileNotFoundException.class, HttpClientErrorException.class }, maxAttempts = 5, backoff = #Backoff(delay = 1000))
public string downloadpdffile(string remoteurl, string pdfname);
}
I have tried referring sites and found using Spring4JunitRunner implementation to test retry. Got confused with implementation. Is it possible to write unit test using Junit 5 mockito for retryable annotations?. Could you please elaborate on the solution here?
You need to use #SpringJUnitConfig (which is the equivalent of the JUnit4 runner). Or #SpringBootTest as you are using Boot.
#Retryable only works with beans managed by Spring - it wraps the bean in a proxy.
#SpringBootApplication
#EnableRetry
public class So71849077Application {
public static void main(String[] args) {
SpringApplication.run(So71849077Application.class, args);
}
}
#Component
class RetryableClass {
private SomeService service;
void setService(SomeService service) {
this.service = service;
}
#Retryable
void retryableMethod(String in) {
service.callme();
throw new RuntimeException();
}
#Recover
void recover(Exception ex, String in) {
service.failed();
}
}
interface SomeService {
void callme();
void failed();
}
#SpringBootTest
class So71849077ApplicationTests {
#MockBean
SomeService service;
#Test
void testRetry(#Autowired RetryableClass retryable) {
SomeService service = mock(SomeService.class);
retryable.setService(service);
retryable.retryableMethod("foo");
verify(service, times(3)).callme();
verify(service).failed();
}
}
I was also trying to implement this using Junit5.
Tried various options but that didn't help. Then after googling for few hours, got the following link and it helped to succeed.
https://doctorjw.wordpress.com/2022/04/29/spring-testing-a-single-bean-in-junit-5-springextension/
Reference code below, for detailed explanation, please refer the blog.
#Component
public class MyClass {
private ObjectMapper objectMapper;
private RestTemplate restTemplate;
#Value("${testValue:5}")
private int value;
#Retryable(....)
public void doStuff() throws SomeException {
...
}
}
What I’ve discovered is, if I declare my test class this way:
#ExtendWith( SpringExtension.class )
#Import( { MyClass.class, ObjectMapper.class } )
#EnableRetry
public class MyClassTest {
#Autowired
private MyClass myClass;
#MockBean
private RestTemplate restTemplate;
#Autowired
private ObjectMapper objectMapper;
#BeforeEach
public void setup() {
// If we are going to jack with the object configuration,
// we need to do so on the actual object, not the Spring proxy.
// So, use AopTestUtils to get around the proxy to the actual obj.
TestingUtils.setFieldValue( AopTestUtils.getTargetObject( myClass ), "value", 10 );
}
}
You will notice the inclusion of 1 other class, TestingUtils.class. This class looks like:
public class TestingUtils {
public static void setFieldValue( Object object, String fieldName, Object value ) {
Field field = ReflectionUtils.findField( object.getClass(), fieldName );
ReflectionUtils.makeAccessible( field );
ReflectionUtils.setField( field, object, value );
}
}
All credits goes to the author of the blog.

Unit Tests How to Mock Repository Using Mockito

I am having an issue with stubbing my repository. I was suggested to just create another application.properties (which I have not done) and to use an in-memory database like H2. I was wondering though if I can just stub the call so when myDataService.findById(id) is called instead of it attempting to get that from the database just a mocked object can be returned?
I am new to writing mocks for my unit tests and spring boot in general so maybe I am missing something. Code below (tried to simplify and made names generic for posting here).
My test class
public class MyServiceImplTest
{
private MyDataService myDataService;
private NyService myService;
private MyRepository myRepository;
#Before
public void setUp() {
myDataService = Mockito.mock(MyDataServiceImpl.class);
myService = new MyServiceImpl(myDataService);
}
#Test
public void getById_ValidId() {
doReturn(MyMockData.getMyObject()).when(myDataService).findById("1");
when(myService.getById("1")).thenReturn(MyMockData.getMyObject());
MyObject myObject = myService.getById("1");
//Whatever asserts need to be done on the object myObject
}
}
Class used for making the service call to the data layer
#Service
public class MyServiceImpl implements MyService {
MyDataService myDataService;
#Autowired
public MyServiceImpl(MyDataService myDataService) {
this.myDataService = myDataService;
}
#Override
public MyObject getById(String id) {
if(id == null || id == "") {
throw new InvalidRequestException("Invalid Identifier");
}
MyObject myObj;
try {
myObj = myDataService.findById(id);
}catch(Exception ex) {
throw new SystemException("Internal Server Error");
}
return myObj;
}
}
This is where I am having the issue in my test. When the findById() method is called, the variable repository is null so when trying to do repository.findOne(id) it throws an exceptionn. This is what I am attempting to mock, but the repository is giving me issues.
#Repository
#Qualifier("MyRepo")
public class MyDataServiceImpl {
#PersistenceContext
private EntityManager em;
private MyRepository repository;
#Autowired
public MyDataServiceImpl(MyRepository repository) {
super(repository);
this.repository = repository;
}
public MyObject findById(String id) {
P persitentObject = repository.findOne(id);
//Calls to map what persitentObject holds to MyObject and returns a MyObject
}
}
Code for MyRepository here just to show it's an empty interface that extends CrudRepository
public interface MyRepository extends CrudRepository<MyObjectPO, String>, JpaSpecificationExecutor<MyObjectPO> {
}
Let me begin by saying you are on the right track by using Constructor Injection and not Field Injection(which makes writing tests with mocks much simpler).
public class MyServiceImplTest
{
private MyDataService myDataService;
private NyService myService;
#Mock
private MyRepository myRepository;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this); // this is needed for inititalizytion of mocks, if you use #Mock
myDataService = new MyDataServiceImpl(myRepository);
myService = new MyServiceImpl(myDataService);
}
#Test
public void getById_ValidId() {
doReturn(someMockData).when(myRepository).findOne("1");
MyObject myObject = myService.getById("1");
//Whatever asserts need to be done on the object myObject
}
}
The call goes all the way from your service --> dataService. But only your repository calls are mocked.
This way you can control and test all the other parts of your classes(both service and dataService) and mock only repository calls.

Injecting DozerBeanMapper with Mockito

I'm using Dozer in my Spring services. How to inject a DozerBeanMapper into a tested service using JUnit and Mockito?
My java class (if simplified) looks like:
#Service
public class UnicornService {
private final DozerBeanMapper dozer;
#Autowired
public UnicornService(DozerBeanMapper dozer) {
this.dozer = dozer;
}
public UnicornDto convert(Unicorn unicorn) {
return dozer.map(unicorn, UnicornDto.class);
}
}
A test class using JUnit 4 + Mockito + Hamcrest looks like:
import static com.shazam.shazamcrest.MatcherAssert.assertThat;
import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Mock
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
#Test
public void testConvert() throws Exception {
final Unicorn original = ...
final UnicornDto expected = ...
// Execute the method being tested
final UnicornDto result = unicornService.convert(original);
// Validation
assertThat(result, sameBeanAs(expected));
}
}
The problem is that a mocked Dozer instance is not mapping objects as expected - by default, Mockito stubs return empty or null objects. And if I remove #Mock annotation from the test, it throws NPE!
Use #Spy annotation on DozerBeanMapper object. This will allow you to call all the normal methods of the object while still this object is managed by Mockito (as a mock) and injected into a tested service.
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Spy
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
// ...
Another solution that I've found is to refactor your code. It seems less attractive to me, because it makes more harm then good, just in the sake of writing tests.
Use injection via setter in the service
#Service
public class UnicornService {
private DozerBeanMapper dozer;
#Autowired
public setDozer(DozerBeanMapper dozer) {
this.dozer = dozer;
}
public UnicornDto convert(Unicorn unicorn) {
return dozer.map(unicorn, UnicornDto.class);
}
}
Refactored test:
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#InjectMocks
private UnicornService unicornService;
#Before
public void injectDozer() {
final DozerBeanMapper dozer = new DozerBeanMapper();
unicornService.setDozer(dozer);
}
// ...
You should not be relying on the mapper creating a proper object at all, just that the service calls the mapper and returns its result. The actual mapping should be tested in a unit test for the mapper. Ie
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Mock
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
#Test
public void testConvert() throws Exception {
final Unicorn original = mock(Unicorn.class);
final UnicornDto expected = mock(UnicornDto.class);
when(dozer.map(original, UnicornDto.class)).thenReturn(expected);
// Execute the method being tested
final UnicornDto result = unicornService.convert(original);
// Validate that the call was delegated to the mapper
assertThat(result, is(expected));
}
}

Junit: testing service with private #Autowired fields

I have been doing Junit tests the past few weeks so my experience, being a junior programmer is fairly limited. After testing the easier service classes in the project, now I am stuck.
The problem is that I can't inject some private final someRepository into the constructor of the service class that I am testing, namely:
#RunWith(SpringRunner.class)
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
#InjectMocks
private SomeService someService;
#Test
public void testMyFunc() {
SomeOtherDto param = new SomeOtherDto();
param.setVar1(...);
param.setVar2(...);
Mockito.when(someRepository.getIt()).thenReturn(-1L);
Mockito.when(someService.myPrivBoolBuilder(param,-1L))
.thenReturn(new BooleanBuilder());
Pageable pageable = null;
Page<SomeDto> result = someService.myFunc(param, pageable);
assertEquals(expResult,
}
/* ... */
}
and the service I am testing:
#Service
#Transactional
public class SomeService implements someAbstractService {
private final CustomMapper customMapper
private final SomeRepository someRepository;
private final SomeOtherRepository someOtherRepository;
#Autowired
public SomeService(final CustomMapper customMapper, final SomeRepository someRepository,
final SomeOtherRepository someOtherRepository, etc)
{ /* ... */ }
public Page<SomeDto> myFunc(final SomeOtherDto param, final Pageable pageable) {
final BooleanBuilder predicate = myPrivBoolBuilder(param,
someOtherRepository.getId());
return someRepository.findAll(predicare, pageable).map(obj -> {
return customMapper.map(obj) });
}
public BooleanBuilder myPrivBoolBuilder(final SomeOtherDto param, final Long id) {
BooleanBuilder predicate = new BooleanBuilder();
final QSomeRepository qSomeRepository = QSomeRepository.someRepository;
final QSomeOtherRepository qSomeOtherRepository = QSomeOtherRepository.someOtherRepository();
predicate.and(qSomeRepository.someField.someOtherField.goe(param.getX()));
predicate.and(qSomeRepository.someField2.someOtherField2.isNotNull()));
predicate.and(qSomeOtherRepository.someField.someOtherField.id.eq(id()
.or(qSomeOtherRepository.someField.someOtherField.id.in(...))));
return predicate;
}
/* ... */
}
My problem is that when I run the test someOtherRepository.getId() return null with SpringRunner.class. When I run with MockitoJUnitRunner.class the someService constructor throws a constructor error: someRepository is NULL
I have tried multiple ways (tried #Spy, #MockBean, Mockito().doReturn... syntax, etc), but these are the two errors I get. I'm pretty sure it's a matter of using the Mocking framework correctly.
If you need other snippets or details, I will kindly offer them.
The reason is that Mockito tries to construct the object because of the #InjectMocks annotation.
SpringRunner is not necessary as this is not a spring test. If you really want a special runner, you can go for MockitoJUnitRunner.
You can simply initialize the Mockito annotations in your Before method and then create your service instance with the constructor providing the mocked dependencies.
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
private SomeService someService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
someService = new SomeService(someRepository);
}
/* ... */
}
You could use ReflectionTestUtil provided by Spring to inject a mock of SomeRepository from the test class.
eg;
ReflectionTestUtils.setField(someService, "someRepository", mock(SomeRepository.class));

Categories

Resources