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

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

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

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


当前回答

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

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剔除抽象方法,这些方法执行断言、抛出异常、返回不同的值等。

其他回答

如果抽象类适合于您的实现,则测试(如上所述)派生的具体类。你的假设是正确的。

为了避免将来的混乱,请注意这个具体的测试类不是模拟的,而是假的。

严格来说,mock由以下特征定义:

A mock is used in place of each and every dependency of the subject class being tested. A mock is a pseudo-implementation of an interface (you may recall that as a general rule, dependencies should be declared as interfaces; testability is one primary reason for this) Behaviors of the mock's interface members -- whether methods or properties -- are supplied at test-time (again, by use of a mocking framework). This way, you avoid coupling of the implementation being tested with the implementation of its dependencies (which should all have their own discrete tests).

首先,如果抽象类包含一些具体的方法,我认为你应该这样做,考虑这个例子

 public abstract class A 

 {

    public boolean method 1
    {
        // concrete method which we have to test.

    }


 }


 class B extends class A

 {

      @override
      public boolean method 1
      {
        // override same method as above.

      }


 } 


  class Test_A 

  {

    private static B b;  // reference object of the class B

    @Before
    public void init()

      {

      b = new B ();    

      }

     @Test
     public void Test_method 1

       {

       b.method 1; // use some assertion statements.

       }

   }

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

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

至于测试抽象测试类,一定要问自己要测试的是什么。有几种方法,您应该找出适合您的场景的方法。您是否试图在子类中测试一个新方法?然后让您的测试只与该方法交互。测试基类中的方法吗?然后可能只针对该类有一个单独的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"));
    }
}