【问题标题】:How to use EasyMock objects in JUnit @Before method as well as test method如何在 JUnit @Before 方法和测试方法中使用 EasyMock 对象
【发布时间】:2020-07-08 05:43:57
【问题描述】:

我正在尝试将 EasyMock 与 JUnit 一起使用,但在调度 JUnit 4 @Before 方法中的模拟依赖项的方法调用时遇到了困难。

在下面的示例中,测试类MockWithBeforeTest 正在测试类ClassUnderTestDependency 被传递给ClassUnderTest 的构造函数,其中调用Dependency 的方法之一,返回初始化ClassUnderTest 所需的值。这个初始化ClassUnderTest的过程对于所有测试都是一样的,所以我用JUnit 4 @Before注解来装饰ClassUnderTest#setUp方法。

在测试方法ClassUnderTest#getDerived 时,我们希望调用模拟的Dependency 实例返回一个值,我们在方法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$$6d3a4341.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$1.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$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.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 的实例来传递给构造函数?

上面给出的示例代表了一个一般问题,其中传递给被测类的依赖关系比Dependency复杂得多。

关于设计问题的推测

我注意到被测类的实现的细节正在通过模拟依赖项上的预定方法泄漏到测试类中。但是,我对模拟框架的经验不足,无法判断这是模拟不可避免的副作用,还是我的设计存在缺陷的症状。任何有关这方面的指导将不胜感激。

软件版本信息

  • Java:1.8.0_201
  • JUnit:4.12
  • EasyMock:4.2

【问题讨论】:

    标签: java unit-testing junit easymock


    【解决方案1】:

    在记录所有内容后,replay() 只能调用一次。这就是为什么它在这里不起作用。

    由于您在构造函数中使用模拟,因此您需要在replay 之后实例化测试的类。

    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.getAClassUnderTest 构造函数中被调用时,这会产生java.lang.IllegalStateException: missing behavior definition for the preceding method call: Dependency.getA()。我相信这是因为在模拟的Dependency 进入MockWithBeforeTest.testGetDerivedreplay 状态之前调用了构造函数。但是,如果按照您的建议,在构造函数中重放所需的方法调用后,模拟无法恢复到“记录”状态,我可能会被卡住。
    • 啊...如果您需要在构造函数中模拟,那就不同了。您将需要在测试中创建测试类。我会更新答案。
    【解决方案2】:

    更多的研究和与同事的讨论产生了解决方案。我错过的步骤是重置模拟的 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);
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-05
      • 2012-07-27
      • 2021-08-27
      • 2016-09-19
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      相关资源
      最近更新 更多