The context
I have a simple test method testFindByUserName. I use mockito library. I have @Mock UserMapper which is created by Mapstruct library.
The problem
Mocito doesn't handle Static method INSTANCE which I use to mapping user to userDto. I have error: error: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: 1. 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. 2. inside when() you don't call method on mock but on some other object.
How resolve this problem.
The code
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class UserServiceImplTest {
private User user;
private String token;
private UserDto userDto;
@InjectMocks
private UserServiceImpl userService;
@Mock
private UserMapper userMapper;
@Before
public void before() {
user = new User(2L, "User_test",
"firstName_test", "lastName_test",
"test@test.pl", true, "password_test",
"address_test", null,new ArrayList<>(),new ArrayList<>(), new HashSet<>());
token = "test_token";
userDto = new UserDto(2L, "User_test",
"firstName_test", "lastName_test",
"test@test.pl", true, "password_test",
"address_test", null,new ArrayList<>(),new ArrayList<>(), new HashSet<>());
}
@Test
public void testFindByUsername() throws Exception {
//Given
String username= "User_test";
when(userMapper.INSTANCE.userToUserDto(userRepository.findByUsername(username))).thenReturn(userDto);
//When
UserDto result = userService.findByUsername(username);
//Then
assertEquals("User_test", result.getUsername());
assertEquals("firstName_test", result.getFirstName());
assertEquals("lastName_test", result.getLastName());
assertEquals("test@test.pl", result.getEmail());
assertEquals("password_test", result.getPassword());
assertEquals("address_test", result.getAddress());
}
method which I testing
@Override
public UserDto findByUsername(final String username) {
User user = userRepository.findByUsername(username);
if (user == null) {
LOGGER.info("Not found user");
}
return mapper.INSTANCE.userToUserDto(user);
}
You need to use PowerMockito to test static methods inside Mockito test, using the following steps:
@PrepareForTest(Static.class) // Static.class contains static methods
Call PowerMockito.mockStatic() to mock a static class (use PowerMockito.spy(class) to mock a specific method):
PowerMockito.mockStatic(Static.class);
Just use Mockito.when() to setup your expectation:
Mockito.when(Static.firstStaticMethod(param)).thenReturn(value);
I solved this by borrowing Spring's AplicationContext mechanism for (transitive) initialization of Mappers.
Disclaimer: this might only work if you use mappers with Spring and componentModel = MappingConstants.ComponentModel.SPRING
Say you have a CarMapper which also uses a transitive WheelMapper:
@Mapper(componentModel = SPRING, uses = {WheelMapper.class})
public interface CarMapper {
CarDto map(Car car);
}
Then in your test code, you create a Spring @Configuration
which imports the generated Mapstruct mapper implemantations (which also comes in handy in your Spring based tests):
@Configuration
@Import({ CarMapperImpl.class, WheelMapperImpl.class })
public class TestMapperConfiguration {}
And then you build a mapper resolver for your tests which uses Spring magic to resolve transitive beans by using the @Configuration
:
public class MapperResolver {
private static ApplicationContext context = buildContext();
private static ApplicationContext buildContext() {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(TestMapperConfiguration.class);
annotationConfigApplicationContext.refresh();
return annotationConfigApplicationContext;
}
public static <T> T getMapper(Class<T> clazz) {
return context.getBean(clazz);
}
}
And finally you can use that resolver in your Mockito tests (this example uses Mockito for JUnit5):
@ExtendWith(MockitoExtension.class)
class MyClazzTest {
@Spy
private CarMapper carMapper = MapperResolver.getMapper(CarMapper.class);
@InjectMocks
private MyClazz sut;
@Test
public void someTest() {
sut.doSomething();
//...
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.