模拟抽象类的非抽象方法

时间:2016-07-31 07:26:56

标签: java unit-testing mocking abstract-class easymock

我正在尝试对扩展抽象基类class的类进行单元测试。 以下是用于说明目的的“类似类”:

public abstract class MyAbstractBaseClass {
  @Autowired
  private WaterFilter waterFilter;

  protected List<String> filterComponents(List<String> allComponents) {
    return waterFilter.filter(allComponents);
  }
}

public class MyDerivedClass extends MyAbstractBaseClass {
  public List<String> filterWater(List<String> allWaterComponents) {
    List<String> filteredComponents = this.filterComponents(allWaterComponents); //calls abstract class's filterComponets()
    filteredComponents.add("something-else");
    return filteredComponents;
  }
}

以下是我正在尝试的单元测试:

    @RunWith(EasyMockRunner.class)
    public class MyDerivedClassTest {
        @TestSubject
        private MyDerivedClassTest SUT;

        @Before
        public void setup() {
           SUT = new MyDerivedClassTest();
        }

        @Test
        public void test filterWater_HappyCase() {
           //I want to mock my abstract class's filterComponents() method
           //I am trying this:
           EasyMock.expect(SUT.filterComponents(getDummyComponents())).andReturn(getSomeComponents());

          //What to replay here?
          //EasyMock.replay(...)

          List<String> actualResult = SUT.filterWater(getDummyComponents());

          //assert something
          //What to verify?
          //EasyMock.verify(...)
    }
}

当我运行此测试时,我得到了

  

显示java.lang.NullPointerException

MyAbstractBaseClass.filter(allComponents)

中的

我知道自动装配的“waterFilter”没有初始化。但是,我只想在单元测试中模拟抽象class的“非抽象”方法。

我应该如何使用EasyMock进行此操作?另外,我不知道replay()verify()会发生什么。

2 个答案:

答案 0 :(得分:3)

当你编写一个单元测试时,你测试一个对象(通常是它的一个方法),你可以模拟一个对象(通常是它的一个方法)。
但是,你不应该单元测试和模拟同一个对象,因为在某种程度上,它似乎不太自然:如果你测试一个类的方法,测试类的行为应该保持尽可能自然而不是伪造自己的方法。
否则,我们可能想知道单元测试的质量是否良好 为什么?因为它没有反映我们在运行时会遇到的类的实际行为,而只是它的一部分行为。 在单元测试中,研究隔离,但在其背后,我们的想法是仅将您的测试类与其他类隔离,而不是孤立其自身的行为。
当然,您可以尝试在被测试类的抽象类中模拟一个非抽象方法,但是测试的设计和质量可能会变得不那么好。

在你的情况下,我想象在抽象类中模拟无抽象方法的两个理由:

  • waterFilter字段依赖项因为未被赋值而烦恼您,因此在测试期间会引发异常(NullPointerException)。
  • 您真的想在抽象类中模拟无抽象方法,因为您已经对此方法进行了单一测试,并且您不想复制此测试。

1)如果您的问题是waterFilter字段依赖关系。

你应该嘲笑waterFilter字段。要模拟字段,它必须是可访问和可修改的。在您的情况下,它不是直的,因为该字段是私有的。

因此,您有两种方法可以访问它以便能够模拟它:

  • 更改您的设计,以便可以从公共方法或MyDerivedClass的构造函数中设置字段。
  • 使用反射设置字段(使用API​​或自己动手,因为它并不难)。

您不需要使用EasyMock验证操作。只需模拟waterFilter.filter(allComponents)返回的结果,例如:

 waterFilterMock.filter(mockedComponents) 

通过这种方式,mock返回您选择的值,在JUnit断言中,您可以对正在测试的方法执行正确的断言。

仅供参考,您可以使用Mockito而不是EasyMock。它更灵活,提供更易读的操作。 例如,您可以使用Mockito执行此操作:

Mockito.when(waterFilterMock.filter()).thenReturn(mockedComponents);

如您所见,它更具可读性。

2)如果你问题是你真的想在抽象类中模拟无抽象方法,因为你已经对它进行了单一测试

您应该修改您的设计并使用合成而不是继承。你不会再有MyAbstractBaseClass,而只是两个类之间的依赖关系(一个有另一个的字段)。通过这种方式,您可以以自然的方式模拟filterComponents()方法。

答案 1 :(得分:0)

必须在模拟资源上设置期望。 在你的情况下,我认为你应该注入一个模拟的WaterFilter实例。

您的期望,重播和验证应该在waterFilter对象实例上设置。

您可以参考下面给出的示例链接。 http://www.tutorialspoint.com/easymock/easymock_junit_integration.htm