簡體   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