我想知道如何单元测试抽象类,以及扩展抽象类的类。

我是否应该通过扩展抽象类来测试它,去掉抽象方法,然后测试所有的具体方法?然后只测试我重写的方法,并在单元测试中测试扩展抽象类的对象的抽象方法?

我是否应该有一个抽象测试用例,可以用来测试抽象类的方法,并在测试用例中为扩展抽象类的对象扩展该类?

注意,我的抽象类有一些具体的方法。


当前回答

我猜你可能想测试一个抽象类的基本功能…但是最好的方法是扩展类而不重写任何方法,并尽可能少地模拟抽象方法。

其他回答

这是我在设置一个测试抽象类的工具时通常遵循的模式:

public abstract class MyBase{
  /*...*/
  public abstract void VoidMethod(object param1);
  public abstract object MethodWithReturn(object param1);
  /*,,,*/
}

以及我在测试中使用的版本:

public class MyBaseHarness : MyBase{
  /*...*/
  public Action<object> VoidMethodFunction;
  public override void VoidMethod(object param1){
    VoidMethodFunction(param1);
  }
  public Func<object, object> MethodWithReturnFunction;
  public override object MethodWithReturn(object param1){
    return MethodWihtReturnFunction(param1);
  }
  /*,,,*/
}

如果在我不期望它的时候调用抽象方法,测试就会失败。在安排测试时,我可以很容易地用lambdas剔除抽象方法,这些方法执行断言、抛出异常、返回不同的值等。

如果您的抽象类包含具有业务价值的具体功能,那么我通常会通过创建一个测试双存根来直接测试它,或者使用一个模拟框架来为我做这件事。我选择哪一个很大程度上取决于我是否需要编写抽象方法的特定于测试的实现。

我需要这样做的最常见的场景是当我使用模板方法模式时,例如当我正在构建某种将由第三方使用的可扩展框架时。在本例中,抽象类定义了我想要测试的算法,因此测试抽象基类比测试特定实现更有意义。

然而,我认为重要的是这些测试应该只关注真实业务逻辑的具体实现;您不应该单元测试抽象类的实现细节,因为您最终会得到脆弱的测试。

我反对“抽象”测试。我认为测试是一个具体的概念,没有抽象的概念。如果您有公共元素,请将它们放在辅助方法或类中供所有人使用。

至于测试抽象测试类,一定要问自己要测试的是什么。有几种方法,您应该找出适合您的场景的方法。您是否试图在子类中测试一个新方法?然后让您的测试只与该方法交互。测试基类中的方法吗?然后可能只针对该类有一个单独的fixture,并使用尽可能多的测试单独测试每个方法。

遵循@patrick-desjardins的回答,我实现了抽象和它的实现类@Test,如下所示:

抽象类- ABC.java

import java.util.ArrayList;
import java.util.List;

public abstract class ABC {

    abstract String sayHello();

    public List<String> getList() {
        final List<String> defaultList = new ArrayList<>();
        defaultList.add("abstract class");
        return defaultList;
    }
}

由于抽象类不能被实例化,但它们可以被子类化,具体类DEF.java如下:

public class DEF extends ABC {

    @Override
    public String sayHello() {
        return "Hello!";
    }
}

测试类来测试抽象方法和非抽象方法:

import org.junit.Before;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.contains;
import java.util.Collection;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;

import org.junit.Test;

public class DEFTest {

    private DEF def;

    @Before
    public void setup() {
        def = new DEF();
    }

    @Test
    public void add(){
        String result = def.sayHello();
        assertThat(result, is(equalTo("Hello!")));
    }

    @Test
    public void getList(){
        List<String> result = def.getList();
        assertThat((Collection<String>) result, is(not(empty())));
        assertThat(result, contains("abstract class"));
    }
}

编写一个Mock对象,并仅用于测试。它们通常是非常非常小的(继承自抽象类),而不是更多。然后,在你的单元测试中,你可以调用你想测试的抽象方法。

您应该测试包含一些逻辑的抽象类,就像您拥有的所有其他类一样。