簡體   English   中英

如何構建多層測試對象?

[英]How do I build a multi-layer test object?

我有一些要測試的類,但是從測試的角度來看,我發現底層實體非常復雜,因為實體可以具有許多層依賴。

例如,我有一個名為Building的類,該類具有一個Parking ,一個Car ,一個Engine ,然后是一個函數,該函數將建築物作為參數並檢查柴油車是否比汽油多。 這意味着在測試中,我需要創建一個Building ,然后在其上附加一個Parking ,然后必須在Car上附加一個依此類推。

有沒有處理深層嵌套實體的標准方法?

如果要測試的函數需要某種類型的對象(即List)的倍數來驗證計數,則有幾種解決方法。

一種方法是僅模擬一堆對象以返回值。 您應該在每個單獨的級別上嘲笑,因此在Building級別上,您應該嘲笑Parking ,而在Parking級別上,您則嘲笑Car

when(car.getEngineType()).thenReturn(EngineType.DIESEL);

但是,如果您希望使用一種模擬較少的方法,則還可以通過使用種子值創建來隔離一種特殊情況。

public class CarTest {

    private EngineType DIESEL = EngineType.DIESEL;

    @Test
    public void testCreate() {
        Car car = createWithSeedValue();
        Assert.equals(DIESEL, car.getEngine().getEngineType());
    }

    public Car createWithSeedValue() {
        return create(EngineTest.createWithSeedValue(DIESEL));
    }

    public Car create(Engine engine) {
        Car car = new Car(engine);
        return car;
    }
}

在這種情況下,我們可以創建一堆我們自己定義的真實Car POJO,而不是創建一堆模擬Car ,而僅提供測試需要的種子值以使其與實際對象配合使用。 這樣做的好處是,與模擬傳遞到列表中的單個對象相比,您可以更輕松地使用實際的POJO實例化列表。

對於具有很多/深層依賴關系的對象,創建單元測試的“標准方法”(即,如果您只想測試Building的功能而不是整體測試 ,那將是集成測試 )是使用模擬

模擬是功能有限的虛擬對象(只需執行給定測試所需的操作)。 通常,模擬對象的每種方法都知道需要輸入什么,您可以在這些輸入和輸出應該之間提供手寫映射。 然后,將它們注入到測試的對象中,而不是實際的依賴關系中。

有許多框架可以幫助您創建模擬對象。

取決於您是否將您視為一個“單元”進行測試。 如果提到的所有這些類都非常緊密地耦合在一起,則可以將它們視為一個單元並一起進行測試。

您也可以考慮將Builder類稱為一個單元,將Parking類稱為另一個單元,等等。在這種情況下,您可以引入Mockito框架。

您將Parking的模擬對象傳遞到Building類(正在測試的類)中。

我更喜歡保持我的“單元”很小,通常僅存在1個類,有時甚至是多個小類。 如果您的單位很小,那么通常您將需要一個模擬框架來使您的測試重點放在這些小單位上。

您可以使用Mockito之類的模擬框架。 然后,您可以模擬類的直接依賴項,並要求依賴項返回您的類可以使用的結果。

與您的方案相關的示例如下

class Building {
    Building(Parking parking) {
    //init
    }

    boolean hasMoreDieselCars() {
        return parking.getDieselCarCount() > parking.getPetrolCarCount();
    }
}

通過模擬,您可以編寫類似這樣的內容。

when(parking.getDieselCarCount()).thenReturn(14);
when(parking.getPetrolCarCount()).thenReturn(11);

同樣,對於單元測試Parking ,它依賴於Car列表,您可以像這樣模擬Car

when(car.getEngine()).thenReturn(new DieselEngine());

希望這可以幫助。

工廠是處理這種情況的好方法(在這里嘲笑肯定是一個過大的殺手):

class ObjectFactory {

    public static Building building() {
        return new Building(parking());
    }

    public static Parking parking() {
        Parking parking = new Parking();
        parking.addCar(car());
        return parking;
    }

    public static Car car() {
        return new Car(engine());
    }

    public static Engine engine() {
        return new Engine();
    }     
}

結合數據隨機化,您可以每次創建不同(但有效)的對象(這可以增加覆蓋范圍)。 例如

public static Parking parking() {
    Parking parking = new Parking();
    for(int i = 0; i < integer(1, 10); i++) parking.addCar(car());
    return parking;
}

public static Car car() {
    Car car = new Car(engine());
    car.setModel(alphanumeric(1, 10));
    return car;
}

我有時甚至將這些方法添加到實體類本身。 是的,這是生產代碼中的僅測試代碼,但看起來確實可讀,隨機化還描述了可以簡化對業務邏輯理解的業務規則。 最終,代碼將如下所示: Parking parking = Parking.random()

如果您需要填充不同口味的對象,則有兩種選擇:

  • 如果在測試中您需要某個字段的特定值(例如,名稱),請創建一個隨機對象並覆蓋該唯一值。
  • 如果您需要一個完全不同的對象層次結構(某些情況下是特殊的),則創建多個方法: random()randomNoAssociations()等。
  • 如果需要一個非常特殊的情況,並且為此創建通用方法沒有意義,則可以在測試類中添加一個私有方法(假設該方法僅用於該類中的測試)。

有時,您需要保留在數據庫中的對象-然后創建一個單獨的工廠(PersistedObjectFactory),該工廠使用原始工廠創建對象,然后將其存儲。

PS:在隨機化之前的示例中,我使用的是Qala Datagen (免責聲明-我是作者)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM