【问题标题】:Mockito when().thenReturn() does not behave as expected when lombok @RequiredArgsConstructor(onConstructor = @__(@Autowired)) is used当使用 lombok @RequiredArgsConstructor(onConstructor = @__(@Autowired)) 时,Mockito when().thenReturn() 的行为不符合预期
【发布时间】:2017-05-31 11:07:39
【问题描述】:

我有Spring课说

@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());
    }

}

这里我的测试用例失败了,但是如果我从 MainServiceImpl 中删除 final 并执行如下明确的 @Autowired

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

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

提前致谢

【问题讨论】:

  • 您的测试没有意义...您正在创建一个模拟并将其注入测试用例而不是MainServiceImpl。所以要么你没有在使用时显示代码,要么你的代码真的很无聊。
  • @M.Deinum 由于安全原因我不能在这里发布实际代码,所以我输入了类似的类。这是一个拼写错误。我已经修改了它。
  • 您还有一个双重初始化,您已经在使用 MockitoJUnitRunner 运行,但再次在您的 @before 方法中初始化它们。你必须初始化它们(特别是由于你的最终成员)。
  • 感谢@M.Deinum,当我删除@RunWith 注释时它工作正常,但是当有@RunWith 注释时它为什么不起作用。如果 JUnit 无法更改最终变量也没关系,但是它如何影响 when().thenReturn() ?
  • 因为你现在正在告诉一个不同的模拟然后MainServiceImpl 有什么要做。你最终得到 2 个模拟(由于双重初始化,你最终得到 2 个模拟,并且由于 final 关键字,它们中只有一个连接到 MainServiceImpl。所以要么删除 @RunWith 要么删除 @987654331 @@BEfore` 方法,你真的只希望这种情况发生一次。

标签: java spring mockito junit4 lombok


【解决方案1】:

lombok 和 Spring @Autowired 无关 @RunWith(MockitoJUnitRunner.class)MockitoAnnotations.initMocks(this); 的组合是问题所在。删除其中任何一个,行为都符合预期。你不需要他们两个。其实MockitoAnnotations.initMocks(this);只存在于你不能使用@RunWith(MockitoJUnitRunner.class)的情况,比如你需要使用SpringRunner.class

这就是它不起作用的原因。 首先,您的所有对象都被实例化。所以你的@Mock 都被创建并注入到你的@InjectMock 对象中:

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

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

如您所见,现在testClassInstance 中的模拟对象和注入到您的被测对象的模拟对象是不同的。

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

【讨论】:

    【解决方案2】:

    这就是你的测试用例应该是什么样子的。你不应该使用

    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),那么将生成只有强制(最终)字段的构造函数。这是因为 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      • 2017-07-21
      相关资源
      最近更新 更多