当前位置: 首页 > 知识库问答 >
问题:

模拟抽象类的非抽象方法

梁昊天
2023-03-14

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

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(所有组件)

我知道自动连线的“滤水器”没有初始化。但接下来,我只想在我的单元测试中模拟抽象的“非抽象”方法。

我该如何使用EasyMock来实现这一点呢?另外,我不知道replay()verify()应该做什么。

共有1个答案

章睿
2023-03-14

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

在您的例子中,我想象了两个原因来模拟抽象类中的no-abstract方法:

  • WaterFilter字段依赖项使您烦恼,因为它没有被值,因此在测试期间引发异常(NullPointerException)。
  • 您确实希望模拟抽象类中的no abstract方法,因为您已经对此方法进行了单元式测试,并且不希望重复此测试。

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()方法。

 类似资料:
  • 这似乎是一个基本问题。但在采访前需要澄清。 我在抽象类中有一个非抽象方法。它的具体类重写了该方法。但我想调用父类的原始方法来调用,而不是重写方法。有什么办法吗? 据我所知,没有办法调用原始方法?

  • 下面是抽象类的代码: 现在,我应该如何为编写单元测试,并且应该如何在中模拟会话? 我在Stackoverflow上尝试了不同的解决方案,但仍然无法模拟它并获得会话模拟。

  • 大家好,我有这个主课堂 错误:(42,8)错误:Home不是抽象的,并且不会覆盖OnFragmentInteractionListener中的onFragmentInteract(String)抽象方法 我创建了一个导航抽屉,并希望有一个新的片段来显示另一个家庭活动的内容。 Android Studio告诉我做个家。类抽象或实现抽象方法。 里面: 我那样做了,但是什么也没有改变。我不能让home类

  • 我有一个子类,它声明了我的抽象超类中的所有方法,但它仍然给我一个错误,说明我的类不是抽象的。我不知道为什么会抛出这个错误。 我得到的具体错误是 PhoneBookEntry.java: 1:错误:PhoneBookEntry不是抽象的,并且不会覆盖可比中的抽象方法compareTo(Object) 我的问题代码: 还有我的子类:

  • 问题内容: Java 8接口默认方法与抽象类中的非抽象方法-两者之间是否有任何区别(除了iface-类,可见性等不同) 默认方法不是Java的后退一步,是否违反Java多年来宣传的本质? 问题答案: 如果抽象子类的具体子类被super()覆盖,则抽象类中的非抽象方法将被调用。因此,存在多种可能性。如果不重写method,则将执行超类方法。如果我们在具体的子类方法中使用super(),则将执行被超类

  • Java 8接口默认方法与抽象类中的非抽象方法--两者之间有什么区别吗(除了iface-class、可见性等的区别之外) 默认方法是不是在Java中倒退了一步,意味着它违背了Java多年来宣传的本质?!