【问题标题】:Mocking a non-abstract method of an abstract class模拟抽象类的非抽象方法
【发布时间】:2016-12-05 12:35:23
【问题描述】:

我正在尝试对扩展抽象基 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() 是什么。

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

    所以,你有两种方法可以访问它来模拟它:

    • 更改您的设计以提供从公共方法或MyDerivedClass 的构造函数中设置字段的可能性。
    • 使用反射设置字段(使用 API 或自己做,因为这并不难)。

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

     waterFilterMock.filter(mockedComponents) 
    

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

    仅供参考,您可以使用 Mockito 代替 EasyMock。它更灵活,并提供更多可读操作。 例如,您可以使用 Mockito 来实现:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      你的期望、回放和验证应该设置在 waterFilter 对象实例上。

      您可以参考以下链接中提供的示例。 http://www.tutorialspoint.com/easymock/easymock_junit_integration.htm

      【讨论】:

      • 如果您提供了链接中的一些代码,而不仅仅是链接本身,这将非常有帮助。链接变得陈旧;您的答案中的代码没有。
      猜你喜欢
      • 1970-01-01
      • 2012-09-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-21
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      相关资源
      最近更新 更多