如何使用JUnit在Java中测试抽象类?

How to test abstract class in Java with JUnit?

对于JUnit的Java测试,我是新手。我必须使用Java,我想使用单元测试。

我的问题是:我有一个带有一些抽象方法的抽象类。但有些方法并不抽象。我如何用JUnit测试这门课?示例代码(非常简单):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Car {

    public Car(int speed, int fuel) {
        this.speed = speed;
        this.fuel = fuel;
    }

    private int speed;
    private int fuel;

    abstract void drive();

    public int getSpeed() {
        return this.speed;
    }

    public int getFuel() {
        return this.fuel;
    }
}

我想测试getSpeed()getFuel()函数。

与这个问题类似的问题在这里,但它没有使用JUnit。

在JUnitFAQ部分,我找到了这个链接,但是我不理解作者想用这个例子说什么。这行代码是什么意思?

1
public abstract Source getSource() ;


如果您没有类的具体实现,并且方法不是static,那么测试它们的意义是什么?如果您有一个具体的类,那么您将作为具体类的公共API的一部分来测试这些方法。

我知道你在想什么,"我不想反复测试这些方法,这就是我创建抽象类的原因",但我的反驳是,单元测试的重点是允许开发人员进行更改、运行测试和分析结果。这些更改的一部分可能包括重写抽象类的方法,包括protectedpublic,这可能导致基本的行为更改。根据这些更改的性质,它可能会影响应用程序以意外的、可能是负面的方式运行的方式。如果您有一个好的单元测试套件,那么这些类型引起的问题在开发时应该很明显。


创建一个继承抽象类的具体类,然后测试具体类从抽象类继承的函数。


对于您发布的示例类,测试getFuel()getSpeed()似乎没有多大意义,因为它们只能返回0(没有setter)。

但是,假设这只是一个用于说明目的的简化示例,并且您有正当理由在抽象基类中测试方法(其他人已经指出了其含义),您可以设置测试代码,以便它创建一个只提供虚拟(不提供op)实现的基类的匿名子类。抽象方法。

例如,在您的TestCase中,您可以这样做:

1
2
3
c = new Car() {
       void drive() { };
   };

然后测试其他方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CarTest extends TestCase
{
    private Car c;

    public void setUp()
    {
        c = new Car() {
            void drive() { };
        };
    }

    public void testGetFuel()
    {
        assertEquals(c.getFuel(), 0);
    }

    [...]
}

(此示例基于JUnit3语法。对于JUnit4,代码会略有不同,但想法是相同的。)


如果您无论如何都需要一个解决方案(例如,因为抽象类的实现太多,测试总是重复相同的过程),那么您可以使用抽象工厂方法创建一个抽象测试类,该方法将由该测试类的实现执行。这个例子适用于testng:

Car的抽象测试类:

1
2
3
4
5
6
7
8
9
10
11
abstract class CarTest {

// the factory method
abstract Car createCar(int speed, int fuel);

// all test methods need to make use of the factory method to create the instance of a car
@Test
public void testGetSpeed() {
    Car car = createCar(33, 44);
    assertEquals(car.getSpeed(), 33);
    ...

执行Car

1
2
3
4
5
6
7
8
9
10
class ElectricCar extends Car {

    private final int batteryCapacity;

    public ElectricCar(int speed, int fuel, int batteryCapacity) {
        super(speed, fuel);
        this.batteryCapacity = batteryCapacity;
    }

    ...

ElectricCar类的单元测试类ElectricCarTest

1
2
3
4
5
6
7
8
9
class ElectricCarTest extends CarTest {

    // implementation of the abstract factory method
    Car createCar(int speed, int fuel) {
        return new ElectricCar(speed, fuel, 0);
    }

    // here you cann add specific test methods
    ...

你可以这样做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public abstract MyAbstractClass {

    @Autowire
    private MyMock myMock;        

    protected String sayHello() {
            return myMock.getHello() +"," + getName();
    }

    public abstract String getName();
}

// this is your JUnit test
public class MyAbstractClassTest extends MyAbstractClass {

    @Mock
    private MyMock myMock;

    @InjectMocks
    private MyAbstractClass thiz = this;

    private String myName = null;

    @Override
    public String getName() {
        return myName;
    }

    @Test
    public void testSayHello() {
        myName ="Johnny"
        when(myMock.getHello()).thenReturn("Hello");
        String result = sayHello();
        assertEquals("Hello, Johnny", result);
    }
}

我将创建一个从抽象类继承的JUnit内部类。这可以被实例化,并且可以访问抽象类中定义的所有方法。

1
2
3
4
5
6
7
8
9
10
public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}


您可以实例化一个匿名类,然后测试该类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    private MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.myDependencyService = new MyDependencyService();
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {    
            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

记住,对于抽象类ClassUnderTest的属性myDependencyService,可见性必须为protected

您还可以将此方法与Mockito巧妙地结合起来。请看这里。


我的测试方法非常简单,在每个abstractUnitTest.java中。我只是在abstractUnitTest.java中创建了一个扩展抽象类的类。然后用这种方法测试。


作为一个选项,您可以在抽象类内创建覆盖逻辑的抽象测试类,并为每个子类测试扩展它。这样,您就可以确保对每个孩子分别测试这个逻辑。


不能测试整个抽象类。在本例中,您有抽象方法,这意味着它们应该由扩展给定抽象类的类实现。

在这个类中,程序员必须编写专用于他的逻辑的源代码。

换句话说,没有测试抽象类的感觉,因为您无法检查抽象类的最终行为。

如果您有与某些抽象类中的抽象方法无关的主要功能,只需创建另一个类,抽象方法将在其中引发一些异常。