Mockito when()。thenReturn()在使用lombok @RequiredArgsConstructor(onConstructor = @__(@ Autowired))时表现不正常

时间:2017-05-31 11:07:39

标签: java spring mockito junit4 lombok

我有春课说

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class MainServiceImpl implements MainService {
    private final InternalService internalService;

    public Set<String> do(String anything) {
      Set<String> relevent = internalService.finaIntern(anything);
      return relevent;
    }
}

我正在编写单元测试案例如下

@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {

    @InjectMocks
    private MainServiceImpl service;

    @Mock
    InternalService internalService;   

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testDo() { 
        Set<String> setData = new HashSet<>();
        setData.add("ABC");
        String a ="a";
        when(internalService.finaIntern(any(String.class))
                                  .thenReturn(setData);
        Set<String> result = service.do(a);
        assertTrue(!result.isEmpty());
    }

}

此处我的测试用例失败了,但是如果我从主要服务中移除最终形式并执行明确的 @Autowired ,如下所示

@Component
class MainServiceImpl implements MainService {
    @Autowired
    private InternalService internalService;
     .....

我很想知道  1.如果删除final关键字,我的测试用例如何通过?  2.使用@RequiredArgsConstructor是一个好习惯,如果是,那么如何,如果没有那么为什么呢?

提前致谢

2 个答案:

答案 0 :(得分:1)

它与lombok和Spring @Autowired无关 @RunWith(MockitoJUnitRunner.class)MockitoAnnotations.initMocks(this);的组合是个问题。删除其中任何一个和行为是预期的。你不需要他们两个。实际上,MockitoAnnotations.initMocks(this);仅适用于无法使用@RunWith(MockitoJUnitRunner.class)的情况,例如,如果您需要使用SpringRunner.class

这就是为什么它不起作用。 首先实例化您的所有对象。因此,您的@Mock都已创建并注入@InjectMock对象:

下面您可以看到,mocks[0]service内的新创建模拟(injectInto),mock是同一个对象。

enter image description here

然后初始化第二次发生。 因此,mockito会创建一个新的@Mock对象,并尝试将其注入已经实例化的@InjectMock对象中。但只要它是最终的,它就无法将它注入现场。 所以这是我们在第二次初始化后得到的结果:

enter image description here 如您所见,现在testClassInstance内部的模拟对象和注入测试对象的模拟是不同的。

@RequiredArgsConstructor怎么样:对我来说,以你的方式使用它是完全可以的。

答案 1 :(得分:0)

这就是您的测试用例的样子。您不应该使用

MockitoAnnotations.initMocks(this)

在将@InjectMock用于要测试的类时。这适用于@AllArgConstructor。

  @RunWith(MockitoJUnitRunner.class)
    class TestMainServiceImpl {

    @InjectMocks
    private MainServiceImpl service;

    @Mock
    InternalService internalService;   


    @Test
    public void testDo() { 
        Set<String> setData = new HashSet<>();
        setData.add("ABC");
        String a ="a";
        when(internalService.finaIntern(any(String.class))
                                  .thenReturn(setData);
        Set<String> result = service.do(a);
        assertTrue(!result.isEmpty());
    }

}

尝试使用上述方法为@allArgConstructor和@RequiredArgConstructor编写测试用例。如果该方法有效,则可以尝试以下逻辑。

如果您在类中仅使用@Data批注(lombok.Data),则将生成仅具有强制性(最终)字段的构造函数。这是因为数据暗示了RequiredArgsConstructor。示例:

@Data
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}

@Builder注释为我们生成MainServiceImpl类,可帮助我们创建新的MainServiceImpl

@Data
@Builder
@Component
class MainServiceImpl implements MainService {
private final InternalService internalService;
}