简体   繁体   English

如何在测试期间使用 Mapstruct 创建的映射器作为@Mock

[英]How to use the mapper created by Mapstruct as @Mock during testing

The context上下文

I have a simple test method testFindByUserName.我有一个简单的测试方法 testFindByUserName。 I use mockito library.我使用 mockito 库。 I have @Mock UserMapper which is created by Mapstruct library.我有由 Mapstruct 库创建的 @Mock UserMapper。

The problem问题

Mocito doesn't handle Static method INSTANCE which I use to mapping user to userDto. Mocito 不处理我用来将用户映射到 userDto 的 Static 方法实例。 I have error: error:org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'.我有错误:error:org.mockito.exceptions.misusing.MissingMethodInvocationException: when() 需要一个必须是“模拟方法调用”的参数。 For example: when(mock.getArticles()).thenReturn(articles);例如:when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods.此外,此错误可能会出现,因为: 1. 您存根以下任一方法:final/private/equals()/hashCode() 方法。 Those methods cannot be stubbed/verified.这些方法不能被存根/验证。 Mocking methods declared on non-public parent classes is not supported.不支持在非公共父类上声明的 Mocking 方法。 2. inside when() you don't call method on mock but on some other object. 2. 在 when() 中,您不会在模拟上调用方法,而是在其他一些 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: 您需要使用PowerMockito通过以下步骤来测试Mockito测试中的静态方法:

 @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()模拟一个静态类(使用PowerMockito.spy(class)模拟一个特定的方法):

 PowerMockito.mockStatic(Static.class); 

Just use Mockito.when() to setup your expectation: 只需使用Mockito.when()来设置您的期望:

 Mockito.when(Static.firstStaticMethod(param)).thenReturn(value); 

I solved this by borrowing Spring's AplicationContext mechanism for (transitive) initialization of Mappers.我通过借用 Spring 的 AplicationContext 机制来对 Mappers 进行(传递)初始化,从而解决了这个问题。

Disclaimer: this might only work if you use mappers with Spring and componentModel = MappingConstants.ComponentModel.SPRING免责声明:这可能仅在您将映射器与 Spring 和componentModel = MappingConstants.ComponentModel.SPRING一起使用时才有效

Say you have a CarMapper which also uses a transitive WheelMapper:假设您有一个 CarMapper,它也使用传递 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):然后在您的测试代码中,您创建一个 Spring @Configuration导入生成的 Mapstruct 映射器实现(这在您的基于 Spring 的测试中也派上用场):

@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 :然后你为你的测试构建一个映射器解析器,它使用 Spring 魔法通过使用@Configuration来解析传递 bean:

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):最后,您可以在 Mockito 测试中使用该解析器(此示例将 Mockito 用于 JUnit5):

@ExtendWith(MockitoExtension.class)
class MyClazzTest {

        @Spy
        private CarMapper carMapper = MapperResolver.getMapper(CarMapper.class);

        @InjectMocks
        private MyClazz sut;

        @Test
        public void someTest() {
            sut.doSomething();
            //...
        }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM