无需使用 easymock 注入模拟
no injection of a mock with easymock
我想使用 easymock 3.5 和 JUnit5 编写一个小示例,但是在尝试注入模拟时出现错误 (nullPointerException)...
这里是测试代码:
package model;
import controler.BookEditor;
import org.easymock.EasyMockRule;
import org.easymock.EasyMockSupport;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import view.BookWindow;
import static org.junit.jupiter.api.Assertions.assertEquals;
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BookTest extends EasyMockSupport {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
public BookWindow bookWindow;
public BookList bookList;
@TestSubject
public BookEditor bookEditor;
@Before
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
@Test
public void testBookCreation() {
Book le_livre_de_la_jungle = new Book("Le livre de la jungle", "Rudyard Kipling",
"Flammarion",
"978-2081263246");
assertEquals(le_livre_de_la_jungle.getTitle(), "Le livre de la jungle");
assertEquals(le_livre_de_la_jungle.getAuthor(), "Rudyard Kipling");
assertEquals(le_livre_de_la_jungle.getEditor(), "Flammarion");
assertEquals(le_livre_de_la_jungle.getISBN(), "978-2081263246");
}
@Test
public void testDisplayBook() {
bookWindow.setTitle("Le livre de la jungle"); //here is line 53
bookWindow.setAuthor("Rudyard Kipling");
bookWindow.setEditor("Flammarion");
bookWindow.setISBN("978-2081263246");
replayAll();
bookEditor.setActiveBook(new Book("Le livre de la jungle",
"Rudyard Kipling", "Flammarion", "978-2081263246"));
verifyAll();
}
}
第一次测试没问题,但是testDisplayBook失败了,因为bookWindow为null。
在我的 POM 中,我有这个:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>4.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.5</version>
<scope>test</scope>
</dependency>
</dependencies>
这里是例外:
java.lang.NullPointerException at
model.BookTest.testDisplayBook(BookTest.java:53) at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at
org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:389)
at
org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at
org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:167)
at
org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at
org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:163)
at
org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:110)
at
org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:57)
at
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute(HierarchicalTestExecutor.java:83)
at
org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77)
at
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null(HierarchicalTestExecutor.java:92)
谢谢。
在 JUnit 5 中 Rules
不能再使用了。你必须 use an Extension
and annotate the test class or method with ExtendWith
. Furthermore you have to use @BeforeEach
instead of @Before
(See also the migration section in the user guide).
更新: 从 EasyMock 4.1 开始,EasyMock 附带了一个开箱即用的 JUnit 5 extension。
据我所知还没有正式的 EasyMock 扩展。 幸运的是 EasyMockRule
可以很容易地移植:
import org.easymock.EasyMockSupport;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
public class EasyMockExtension implements TestInstancePostProcessor {
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
EasyMockSupport.injectMocks(testInstance);
}
}
现在您可以为测试添加注释 class:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
// ...
@ExtendWith(EasyMockExtension.class)
public class BookTest extends EasyMockSupport {
@Mock
public BookWindow bookWindow;
public BookList bookList;
@TestSubject
public BookEditor bookEditor;
@BeforeEach
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
// ...
使用 JUnit Jupiter 的方法
我认为您的代码的问题之一是它混合使用了 JUnit 4 和 JUnit 5 注释。根据您的堆栈跟踪,我相信您使用的是 JUnit 5 测试引擎,因此您应该将注释 @Before
替换为 @BeforeEach
。
我使用支持 JUnit 的 Eclipse Oxygen 重现了您的问题。
我通过用显式实例化替换基于注解的注入来超越 NullPointerException
:
public class BookTest extends EasyMockSupport {
public BookWindow bookWindow;
public BookList bookList;
public BookEditor bookEditor;
@BeforeEach
public void setUp() {
bookWindow = EasyMock.mock(BookWindow.class);
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
...
}
我没有 Book
、BookList
、BookEditor
和 BookWindow
的实现,所以我无法验证测试是否通过,但至少它通过了运行并可以定义 bookWindow
。
使用 JUnit 4 的方法
话虽如此,我尝试使用 JUnit 4 执行您的代码(将 JUnit Jupiter @Test
注释替换为 @org.junit.Test
。这产生了以下异常:
java.lang.NullPointerException: Have you forgotten to instantiate bookEditor?
at org.easymock.internal.Injector.injectMocks(Injector.java:81)
at org.easymock.EasyMockSupport.injectMocks(EasyMockSupport.java:561)
at org.easymock.internal.EasyMockStatement.evaluate(EasyMockStatement.java:42)
...
EasyMock 检查 @TestSubject
在注入模拟之前实例化。我相信您不想实例化 BookEditor
before 注入模拟,因为无论如何您最终都会得到 null
bookWindow
:
public class BookTest extends EasyMockSupport {
...
@Mock
public BookWindow bookWindow;
@TestSubject // BookWindow would be null below as not yet injected.
public BookEditor bookEditor = new BookEditor(bookList, bookWindow);
一种可能是从 bookEditor
实例变量中删除 @TestSubject
注释,并在 @Before
方法中实例化 BookEditor
(正如您目前所做的那样) :
public class BookTest extends EasyMockSupport {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
public BookWindow bookWindow;
public BookList bookList;
public BookEditor bookEditor;
@Before
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
...
但是你可能需要@TestSubject
,在这种情况下这个解决方案是不可行的。
另一种方法是避免 @Mock
注释并显式实例化模拟:
public class BookTest extends EasyMockSupport {
...
public BookWindow bookWindow = EasyMock.mock(BookWindow.class);
@TestSubject
public BookEditor bookEditor = new BookEditor(bookList, bookWindow);
...
我想使用 easymock 3.5 和 JUnit5 编写一个小示例,但是在尝试注入模拟时出现错误 (nullPointerException)...
这里是测试代码:
package model;
import controler.BookEditor;
import org.easymock.EasyMockRule;
import org.easymock.EasyMockSupport;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import view.BookWindow;
import static org.junit.jupiter.api.Assertions.assertEquals;
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BookTest extends EasyMockSupport {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
public BookWindow bookWindow;
public BookList bookList;
@TestSubject
public BookEditor bookEditor;
@Before
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
@Test
public void testBookCreation() {
Book le_livre_de_la_jungle = new Book("Le livre de la jungle", "Rudyard Kipling",
"Flammarion",
"978-2081263246");
assertEquals(le_livre_de_la_jungle.getTitle(), "Le livre de la jungle");
assertEquals(le_livre_de_la_jungle.getAuthor(), "Rudyard Kipling");
assertEquals(le_livre_de_la_jungle.getEditor(), "Flammarion");
assertEquals(le_livre_de_la_jungle.getISBN(), "978-2081263246");
}
@Test
public void testDisplayBook() {
bookWindow.setTitle("Le livre de la jungle"); //here is line 53
bookWindow.setAuthor("Rudyard Kipling");
bookWindow.setEditor("Flammarion");
bookWindow.setISBN("978-2081263246");
replayAll();
bookEditor.setActiveBook(new Book("Le livre de la jungle",
"Rudyard Kipling", "Flammarion", "978-2081263246"));
verifyAll();
}
}
第一次测试没问题,但是testDisplayBook失败了,因为bookWindow为null。
在我的 POM 中,我有这个:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>4.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.5</version>
<scope>test</scope>
</dependency>
</dependencies>
这里是例外:
java.lang.NullPointerException at model.BookTest.testDisplayBook(BookTest.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:389) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:167) at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:163) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:110) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute(HierarchicalTestExecutor.java:83) at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:77) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null(HierarchicalTestExecutor.java:92)
谢谢。
在 JUnit 5 中 Rules
不能再使用了。你必须 use an Extension
and annotate the test class or method with ExtendWith
. Furthermore you have to use @BeforeEach
instead of @Before
(See also the migration section in the user guide).
更新: 从 EasyMock 4.1 开始,EasyMock 附带了一个开箱即用的 JUnit 5 extension。
据我所知还没有正式的 EasyMock 扩展。 幸运的是 EasyMockRule
可以很容易地移植:
import org.easymock.EasyMockSupport;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
public class EasyMockExtension implements TestInstancePostProcessor {
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
EasyMockSupport.injectMocks(testInstance);
}
}
现在您可以为测试添加注释 class:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
// ...
@ExtendWith(EasyMockExtension.class)
public class BookTest extends EasyMockSupport {
@Mock
public BookWindow bookWindow;
public BookList bookList;
@TestSubject
public BookEditor bookEditor;
@BeforeEach
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
// ...
使用 JUnit Jupiter 的方法
我认为您的代码的问题之一是它混合使用了 JUnit 4 和 JUnit 5 注释。根据您的堆栈跟踪,我相信您使用的是 JUnit 5 测试引擎,因此您应该将注释 @Before
替换为 @BeforeEach
。
我使用支持 JUnit 的 Eclipse Oxygen 重现了您的问题。
我通过用显式实例化替换基于注解的注入来超越 NullPointerException
:
public class BookTest extends EasyMockSupport {
public BookWindow bookWindow;
public BookList bookList;
public BookEditor bookEditor;
@BeforeEach
public void setUp() {
bookWindow = EasyMock.mock(BookWindow.class);
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
...
}
我没有 Book
、BookList
、BookEditor
和 BookWindow
的实现,所以我无法验证测试是否通过,但至少它通过了运行并可以定义 bookWindow
。
使用 JUnit 4 的方法
话虽如此,我尝试使用 JUnit 4 执行您的代码(将 JUnit Jupiter @Test
注释替换为 @org.junit.Test
。这产生了以下异常:
java.lang.NullPointerException: Have you forgotten to instantiate bookEditor?
at org.easymock.internal.Injector.injectMocks(Injector.java:81)
at org.easymock.EasyMockSupport.injectMocks(EasyMockSupport.java:561)
at org.easymock.internal.EasyMockStatement.evaluate(EasyMockStatement.java:42)
...
EasyMock 检查 @TestSubject
在注入模拟之前实例化。我相信您不想实例化 BookEditor
before 注入模拟,因为无论如何您最终都会得到 null
bookWindow
:
public class BookTest extends EasyMockSupport {
...
@Mock
public BookWindow bookWindow;
@TestSubject // BookWindow would be null below as not yet injected.
public BookEditor bookEditor = new BookEditor(bookList, bookWindow);
一种可能是从 bookEditor
实例变量中删除 @TestSubject
注释,并在 @Before
方法中实例化 BookEditor
(正如您目前所做的那样) :
public class BookTest extends EasyMockSupport {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
public BookWindow bookWindow;
public BookList bookList;
public BookEditor bookEditor;
@Before
public void setUp() {
bookList = new BookList();
bookEditor = new BookEditor(bookList, bookWindow);
}
...
但是你可能需要@TestSubject
,在这种情况下这个解决方案是不可行的。
另一种方法是避免 @Mock
注释并显式实例化模拟:
public class BookTest extends EasyMockSupport {
...
public BookWindow bookWindow = EasyMock.mock(BookWindow.class);
@TestSubject
public BookEditor bookEditor = new BookEditor(bookList, bookWindow);
...