在下面的测试用例中获取 NotAMockException
Getting NotAMockException on the below Test Case
我已尝试 运行 下面的测试,但遇到 NotAMock
异常,但不确定如何解决。我一直在尝试阅读不能模拟 class 被测方法的概念,但我无法弄清楚这个问题。如果有人能用我自己的例子向我解释为什么,我希望能更好地理解它。
我尝试了各种方法来更改 @RunWith
运行 单元或集成测试设置的人员,或者使用 @Spy
而不是 @Mock
或没有 @Autowired
等等,但要么面临 dao 空指针,要么可变地不是模拟异常。
我是否应该使用另一个 class 并在 class 中注入 Listener
并模拟侦听器以实现能够模拟方法并捕获参数的功能动态地。这会起作用吗,因为它不再是 class 被测试的,因此可以模拟这些方法?如果是这样,这是如何实现的。如果不是,什么是正确的方法。我的感觉是将侦听器移动到另一个 class 只会扩展我当前无法模拟但无法解决的问题集。但是,我不确定什么是正确的结果。
@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 @Autowired private FetchFileDetailsDAOImpl dao;
改用@Mock private FetchFileDetailsDAOImpl dao;
你在这里搞混了。 @Mock
和 @MockBean
之间有一个重要的区别。
如果您想在没有任何 Spring 上下文支持(说 @SpringBootTest
、@DataJpaTest
等)的情况下编写单元测试,请使用第一个注释。对于此类测试,您可以使用 @Mock
和 @InjectMocks
.
在您编写集成测试时(您正在使用 @SpringBootTest
启动整个上下文),您在测试中使用托管 Spring beans。因此,您不再编写 unit 测试。
如果你想在你的 Spring 测试上下文中用它的模拟版本替换 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())
}
开始整个上下文但是对于此测试来说是恕我直言矫枉过正。你最好用 just JUnit 和 Mockito 编写一个好的旧单元测试。
除此之外,我还会重新考虑您当前的测试会给您的项目带来什么好处,因为它实际上是在复制业务逻辑。也许这里没有更多代码。
您可以找到 difference between @Mock and @MockBean in this article 的更详细摘要。
我已尝试 运行 下面的测试,但遇到 NotAMock
异常,但不确定如何解决。我一直在尝试阅读不能模拟 class 被测方法的概念,但我无法弄清楚这个问题。如果有人能用我自己的例子向我解释为什么,我希望能更好地理解它。
我尝试了各种方法来更改 @RunWith
运行 单元或集成测试设置的人员,或者使用 @Spy
而不是 @Mock
或没有 @Autowired
等等,但要么面临 dao 空指针,要么可变地不是模拟异常。
我是否应该使用另一个 class 并在 class 中注入 Listener
并模拟侦听器以实现能够模拟方法并捕获参数的功能动态地。这会起作用吗,因为它不再是 class 被测试的,因此可以模拟这些方法?如果是这样,这是如何实现的。如果不是,什么是正确的方法。我的感觉是将侦听器移动到另一个 class 只会扩展我当前无法模拟但无法解决的问题集。但是,我不确定什么是正确的结果。
@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 @Autowired private FetchFileDetailsDAOImpl dao;
改用@Mock private FetchFileDetailsDAOImpl dao;
你在这里搞混了。 @Mock
和 @MockBean
之间有一个重要的区别。
如果您想在没有任何 Spring 上下文支持(说 @SpringBootTest
、@DataJpaTest
等)的情况下编写单元测试,请使用第一个注释。对于此类测试,您可以使用 @Mock
和 @InjectMocks
.
在您编写集成测试时(您正在使用 @SpringBootTest
启动整个上下文),您在测试中使用托管 Spring beans。因此,您不再编写 unit 测试。
如果你想在你的 Spring 测试上下文中用它的模拟版本替换 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())
}
开始整个上下文但是对于此测试来说是恕我直言矫枉过正。你最好用 just JUnit 和 Mockito 编写一个好的旧单元测试。
除此之外,我还会重新考虑您当前的测试会给您的项目带来什么好处,因为它实际上是在复制业务逻辑。也许这里没有更多代码。
您可以找到 difference between @Mock and @MockBean in this article 的更详细摘要。