java - 模拟抽象类的非抽象方法

标签 java unit-testing mocking abstract-class easymock

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

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”未初始化。但是,我只想在单元测试中模拟抽象类的“非抽象”方法。

我应该如何使用 EasyMock 来解决这个问题?另外,我不知道要 replay()verify() 做什么。

最佳答案

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

就您而言,我想有两个理由来模拟抽象类中的非抽象方法:

  • waterFilter 字段依赖项会让您烦恼,因为它没有被重视,因此在测试期间会引发异常 (NullPointerException)。
  • 您确实想模拟抽象类中的非抽象方法,因为您已经统一测试了该方法,并且不想重复该测试。

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

您应该模拟waterFilter字段。要模拟字段,它必须是可访问和可修改的。就您而言,这并不直接,因为该字段是私有(private)的。

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

  • 更改您的设计,以便可以通过公共(public)方法或在 MyDerivedClass 的构造函数中设置字段。
  • 使用反射来设置字段(使用 API 或自己完成,因为这并不难)。

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

 waterFilterMock.filter(mockedComponents) 

通过这种方式,模拟会返回您选择的值,并且在 JUnit 断言中,您可以为被测方法执行正确的断言。

仅供引用,您可以使用 Mockito 而不是 EasyMock。它更加灵活并且提供了更多可读的操作。 例如,您可以使用 Mockito 来做到这一点:

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

正如您所看到的,它更具可读性。

2)如果您的问题是您确实想模拟抽象类中的非抽象方法,因为您已经对其进行了统一测试

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

关于java - 模拟抽象类的非抽象方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38681748/

相关文章:

java - 使用转义引号分割逗号分隔的字符串

java - 为什么这段代码的输出不同?

java - 连接图中最近点的最佳方法

javascript - 在单元测试中观察 AngularJS 属性时,newValue 始终等于 oldValue

android - fragment 单元测试 : launchFragment throws ClassCastException

c++ - 如何模拟 malloc 以在 GMOCK 中返回 null?

testing - 如何在 Haskell 中模拟测试?

java - JTest - try catch block 中的变量可能为空

.net - 在构建服务器上为 Windows 应用商店单元测试自动获取开发人员许可证

java - 捕获先前的值以验证模拟对象