java - 在下面的测试用例中获取 NotAMockException

标签 java mockito junit4 spring-boot-test

我尝试运行以下测试,但遇到 NotAMock 异常,并且不确定如何解决它。我一直在尝试理解被测试类的方法不能被 mock 的概念,但我无法弄清楚这个主题。如果有人可以用我自己的例子向我解释为什么,我希望能更好地理解它。

我尝试了各种方法来更改单元或集成测试设置的@RunWith运行器或使用@Spy而不是@Mock或没有@Autowired 等,但要么面临 dao 空指针,要么面临不同的模拟异常。

我是否应该使用另一个类并在该类中注入(inject)Listener并模拟监听器以实现能够模拟方法并动态捕获参数的功能。这会起作用吗,因为它不再是被测试的类,因此可以模拟这些方法?如果是的话,这是如何实现的。如果不是,什么是正确的方法。我的感觉是,将监听器移至另一个类只会扩展我当前无法模拟的一系列问题,但无法解决它。但是,我不确定什么是正确的结果。

@Component
public class FileEventListener implements ApplicationListener<FileEvent> {

    @Autowired private FetchFileDetailsDAO fileDao;//Dao is annotated with @Transactional

    @Override
    public void onApplicationEvent(FileEvent event) {
        fileDao.getDetailsForFile(event.fileName())
    }     
}
-----------------------------------------------------------------------------------------

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest(classes = TestApp.class)
@RunWith(SpringRunner.class)
public class TestClass {  
  
@Captor private ArgumentCaptor<Object> captor;
@Mock @Autowired private FetchFileDetailsDAO dao;
@InjectMocks @Autowired private FileEventListener listener;

@Before
public void setup() throws IOException {
    MockitoAnnotations.initMocks(this);
}

@Test
@Transactional
@Rollback(true)
public void test() throws Exception {
    FileEvent ev = new FileEvent();
    ...
    listener.onApplicationEvent(ev);
    verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}

最佳答案

你把事情搞混了。 @Mock@MockBean 之间有一个重要的区别。

如果您想编写一个没有任何 Spring Context 支持的单元测试(如 @SpringBootTest@DataJpaTest 等),则可以使用第一个注释。对于此类测试,您可以使用 @Mock@InjectMocks

当您编写集成测试时(您使用 @SpringBootTest 启动整个上下文),您可以在测试中使用托管 Spring bean。因此,您不再编写单元测试。

如果您想在 Spring 测试上下文中用 Spring bean 的模拟版本替换 Spring bean,则必须使用 @MockBean:

@SpringBootTest(classes = TestApp.class)
@RunWith(SpringRunner.class)
@RunWith(MockitoJUnitRunner.class) // will do the Captor initialization for you
public class TestClass {  

  @Captor 
  private ArgumentCaptor<Object> captor;

  @MockBean 
  private FetchFileDetailsDAO dao;

  @Autowired 
  private FileEventListener listener;


  @Test
  @Transactional
  @Rollback(true)
  public void test() throws Exception {
    FileEvent ev = new FileEvent();
    // ...
    listener.onApplicationEvent(ev);
    verify(dao, times(1)).getDetailsForFile((String)captor.capture())
  }

但是对于这个测试来说,启动整个上下文是恕我直言的过度杀伤力。您最好仅使用 JUnit 和 Mockito 编写一个好的旧单元测试。

除此之外,我还会重新考虑当前的测试会给您的项目带来什么好处,因为它实际上是在复制业务逻辑。也许还有更多代码没有出现在这里。

您可以找到 difference between @Mock and @MockBean in this article 的更详细摘要.

关于java - 在下面的测试用例中获取 NotAMockException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64725181/

相关文章:

java - Mockito:模拟包含 lambda 表达式的方法调用

java - JUNIT 与 Excel 输入数据

java - 使用 FEST 进行多个 JUnit 测试

对注入(inject)了另一个 ejb 的 ejb3.0 进行单元测试

java - JLabel 保留以前的文本

java - RichFaces 4.2 转发到新页面,h :commandLink

java - 解码多个元素的同名对象

java - Mockito 具有静态类和返回 void 的方法

java - 在调用验证方法后更改值属性时,ArgumentCaptor.getValue() 上的 assertThat 误报

java - 如何在java中读取循环内带有空格的字符串?