繁体   English   中英

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

[英]How to test abstract class in Java with JUnit?

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

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

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()函数。

与此问题类似的问题是here ,但它没有使用JUnit。

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

public abstract Source getSource() ;

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

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

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

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

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

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

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

然后测试其余的方法,例如:

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的抽象测试类:

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的实现

class ElectricCar extends Car {

    private final int batteryCapacity;

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

    ...

ElectricCar ElectricCarTest

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
    ...

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

public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}

你可以做这样的事情

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);
    }
}

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

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);
    }
}

请记住,必须protected抽象类ClassUnderTest的属性myDependencyService的可见性。

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

我的测试方法非常简单,在每个abstractUnitTest.java中。 我只是在 abstractUnitTest.java 中创建一个扩展抽象类的类。 并以这种方式进行测试。

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

你不需要一个花哨的 Mockito 插件,或者匿名类,或者其他答案推荐的任何其他东西。 Junit 支持相互扩展的测试类:因此,为您的抽象基类编写一个彻底的抽象测试类(实际上只是使测试类抽象),它检查每个方法的行为方式。 对一组实例变量对象执行这些测试,这些对象是在此基础测试类的 @BeforeEach 方法中根据需要设置的。 让该方法调用一个抽象的 allocateObjects() 方法,该方法将完成所有对象分配。

然后,对于扩展您的抽象基础的每个类,都有一个扩展您刚刚编写的抽象测试类的测试类。 这个测试类将在被覆盖的 allocateObjects() 方法中进行实际的对象分配。 它分配的对象将由父测试类中的方法使用:由该测试类继承的方法,因此作为其测试的一部分运行。

你会做工厂傻事吗? 我猜:但是由于您可能无论如何都需要为每个子类创建测试类,所以您最好通过继承保持简单。 我想如果你有很多子类,并且它们都没有做任何值得从超类的东西测试的事情,那将是值得的:但是在那种情况下你到底为什么要创建子类呢?

不要在抽象类上进行@inject 模拟,而是创建一个间谍并在测试类本身中创建一个匿名实现,并使用它来测试你的抽象类。最好不要这样做,因为你可以做单元上不应该有任何公共方法test.Keep 它受保护并从已实现的类中调用这些方法并仅测试这些类。

你不能测试整个抽象类。 在这种情况下,您有抽象方法,这意味着它们应该由扩展给定抽象类的类来实现。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM