如何在JUnit @Before 方法和测试方法中使用EasyMock 对象
How to use EasyMock objects in JUnit @Before method as well as test method
我正在尝试将 EasyMock 与 JUnit 一起使用,但在安排对 JUnit 4 @Before
方法中的模拟依赖项的方法调用时遇到了困难 运行。
在下面的示例中,测试 class MockWithBeforeTest
正在测试 class ClassUnderTest
。 Dependency
被传递给 ClassUnderTest
的构造函数,在其中调用 Dependency
的方法之一,return 初始化 ClassUnderTest
所需的值。这个初始化 ClassUnderTest
的过程对于所有测试都是相同的,所以我用 JUnit 4 @Before
注释装饰 ClassUnderTest#setUp
方法。
在测试方法 ClassUnderTest#getDerived
时,我们期望调用模拟的 Dependency
实例以 return 一个值,我们在方法 MockWithBeforeTest#testGetDerived
中安排了该值。但是,此测试意外失败并显示错误 Unexpected method call Dependency.getB()
,尽管此调用已安排在 MockWithBeforeTest#testGetDerived
.
中
我应该如何修改示例代码以使 MockWithBeforeTest#testGetDerived
通过?
示例代码
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMockRule;
import org.easymock.Mock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
verify(this.dependency);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
class ClassUnderTest {
private int a;
private Dependency dependency;
ClassUnderTest(Dependency dependency) {
this.a = dependency.getA();
this.dependency = dependency;
}
void setA(int val) {
this.a = val;
}
int getDerived(int val) {
return val * this.a * this.dependency.getB();
}
}
class Dependency {
private int a;
private int b;
Dependency(int a, int b) {
this.a = a;
this.b = b;
}
int getA() {
return this.a;
}
int getB() {
return this.b;
}
}
堆栈跟踪
java.lang.AssertionError:
Unexpected method call Dependency.getB():
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:101)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
at Dependency$$EnhancerByCGLIB$d3a4341.getB(<generated>)
at MockWithBeforeTest.testGetDerived(MockWithBeforeTest.java:33)
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.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.easymock.internal.EasyMockStatement.evaluate(EasyMockStatement.java:43)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
为什么不创建一个 Dependency
的实例来传递给构造函数?
上面给出的例子代表了一个一般问题,其中传递给被测class的依赖比Dependency
复杂得多。
关于设计问题的推测
我注意到以下事实:class 正在测试的 实现 的详细信息正在通过模拟上的预定方法泄漏到测试 class 中依赖。然而,我对模拟框架没有足够的经验来判断这是模拟不可避免的副作用,还是我设计缺陷的症状。对此的任何指导将不胜感激。
软件版本信息
- Java: 1.8.0_201
- JUnit: 4.12
- EasyMock: 4.2
replay()
只能在记录完所有内容后调用一次。这就是它在这里不起作用的原因。
因为,在您的情况下,您正在构造函数中使用模拟,因此您需要在 replay
之后实例化经过测试的 class。
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
您也可以在两者之间重置模拟,但我不确定我是否喜欢那样。它会让您通过构造函数,然后使用新记录将模拟重新用于实际测试。
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
reset(this.dependency);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
更多的研究和与同事的讨论产生了一个解决方案。我错过的步骤是重置模拟的 Dependency
对象 using EasyMock.reset(this.dependency)
以允许在测试方法中添加额外的预期调用。固定的MockWithBeforeTest
是
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
verify(this.dependency);
reset(this.dependency); // Allow additional expected method calls to be specified
// in the test methods
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
我正在尝试将 EasyMock 与 JUnit 一起使用,但在安排对 JUnit 4 @Before
方法中的模拟依赖项的方法调用时遇到了困难 运行。
在下面的示例中,测试 class MockWithBeforeTest
正在测试 class ClassUnderTest
。 Dependency
被传递给 ClassUnderTest
的构造函数,在其中调用 Dependency
的方法之一,return 初始化 ClassUnderTest
所需的值。这个初始化 ClassUnderTest
的过程对于所有测试都是相同的,所以我用 JUnit 4 @Before
注释装饰 ClassUnderTest#setUp
方法。
在测试方法 ClassUnderTest#getDerived
时,我们期望调用模拟的 Dependency
实例以 return 一个值,我们在方法 MockWithBeforeTest#testGetDerived
中安排了该值。但是,此测试意外失败并显示错误 Unexpected method call Dependency.getB()
,尽管此调用已安排在 MockWithBeforeTest#testGetDerived
.
我应该如何修改示例代码以使 MockWithBeforeTest#testGetDerived
通过?
示例代码
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMockRule;
import org.easymock.Mock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
verify(this.dependency);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
class ClassUnderTest {
private int a;
private Dependency dependency;
ClassUnderTest(Dependency dependency) {
this.a = dependency.getA();
this.dependency = dependency;
}
void setA(int val) {
this.a = val;
}
int getDerived(int val) {
return val * this.a * this.dependency.getB();
}
}
class Dependency {
private int a;
private int b;
Dependency(int a, int b) {
this.a = a;
this.b = b;
}
int getA() {
return this.a;
}
int getB() {
return this.b;
}
}
堆栈跟踪
java.lang.AssertionError:
Unexpected method call Dependency.getB():
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:101)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
at Dependency$$EnhancerByCGLIB$d3a4341.getB(<generated>)
at MockWithBeforeTest.testGetDerived(MockWithBeforeTest.java:33)
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.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.easymock.internal.EasyMockStatement.evaluate(EasyMockStatement.java:43)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
为什么不创建一个 Dependency
的实例来传递给构造函数?
上面给出的例子代表了一个一般问题,其中传递给被测class的依赖比Dependency
复杂得多。
关于设计问题的推测
我注意到以下事实:class 正在测试的 实现 的详细信息正在通过模拟上的预定方法泄漏到测试 class 中依赖。然而,我对模拟框架没有足够的经验来判断这是模拟不可避免的副作用,还是我设计缺陷的症状。对此的任何指导将不胜感激。
软件版本信息
- Java: 1.8.0_201
- JUnit: 4.12
- EasyMock: 4.2
replay()
只能在记录完所有内容后调用一次。这就是它在这里不起作用的原因。
因为,在您的情况下,您正在构造函数中使用模拟,因此您需要在 replay
之后实例化经过测试的 class。
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
您也可以在两者之间重置模拟,但我不确定我是否喜欢那样。它会让您通过构造函数,然后使用新记录将模拟重新用于实际测试。
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
reset(this.dependency);
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}
更多的研究和与同事的讨论产生了一个解决方案。我错过的步骤是重置模拟的 Dependency
对象 using EasyMock.reset(this.dependency)
以允许在测试方法中添加额外的预期调用。固定的MockWithBeforeTest
是
public class MockWithBeforeTest {
@Rule
public EasyMockRule rule = new EasyMockRule(this);
@Mock
private Dependency dependency;
private ClassUnderTest classUnderTest;
@Before
public void setUp() {
expect(this.dependency.getA()).andReturn(2);
replay(this.dependency);
this.classUnderTest = new ClassUnderTest(this.dependency);
verify(this.dependency);
reset(this.dependency); // Allow additional expected method calls to be specified
// in the test methods
}
@Test
public void testGetDerived() {
expect(this.dependency.getB()).andReturn(3);
replay(this.dependency);
assertEquals(6, this.classUnderTest.getDerived(1));
verify(this.dependency);
}
}