如何构建多层测试对象?

时间:2016-11-23 15:50:33

标签: java junit mockito

我有一些我想测试的类,但是从测试的角度来看,我发现底层实体非常复杂,因为entites可以有很多层依赖。

例如,我有一个名为Building的类,其中ParkingCar个,有Engine个,然后是一个函数建筑物作为参数并检查柴油车是否比汽油更多。这意味着在我的测试中,我需要创建一个Building,然后附加一个Parking,我必须附加一个Car,依此类推。

有没有任何标准的方法来处理深层嵌套的实体?

5 个答案:

答案 0 :(得分:1)

如果你正在测试一个需要多种类型对象(即List)的函数来验证计数,那么有几种方法可以解决它。

一种方法是模拟一堆对象来返回值。你应该在每个级别进行嘲弄,所以在Building级别,你是在嘲笑Parking,而在Parking级别,你是在嘲笑{ {1}}秒。

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; } } POJO填充车库,而不是创建一堆模拟Car,只提供我们需要的种子值用于处理实际对象的测试。这样做的好处是,您可以更轻松地使用实际POJO实例化列表,而不必模拟传递到列表中的单个对象。

答案 1 :(得分:0)

创建单元测试的“标准方式”(即,如果您只想测试Building的功能而不是整体测试 - 那将是集成测试)对于具有许多/深度依赖性的对象正在使用mocks

模拟是具有有限功能的虚拟对象(只需执行给定测试所需的操作)。通常,模拟对象的每个方法都知道期望什么输入,并且您提供这些输入与输出之间的手写映射。然后将这些注入到测试对象中而不是实际的依赖项中。

有许多框架可以帮助您创建模拟对象。

答案 2 :(得分:0)

取决于你想要测试的内容,如果你被视为一个单位'。如果提到的所有类都非常强耦合,您可以认为它们是一个单元并一起测试它们。

你也可以考虑调用Builder类一个单元,停放类另一个单元等。在这种情况下,你可以引入mockito框架。

您将一个模拟的Parking对象传递给您的Building类(您正在测试的类)。

我更愿意保留我的单位'小的,通常仅存在一类,或者有时是多个小类。如果你保持你的单位小,你经常需要一个模拟框架来让你的测试集中在那些小单位上。

答案 3 :(得分:0)

您可以使用像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());

希望这有帮助。

答案 4 :(得分:0)

工厂是处理这种情况的好方法(嘲笑在这里肯定是一种矫枉过正的行为):

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

结合数据随机化,您可以每次创建不同(但有效)的对象(这可以增加覆盖范围)。 E.g。

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()等。
  • 如果需要一个非常特殊的情况并且创建一个通用方法没有意义,可以在测试类中添加一个私有方法(假设它仅用于该类中的测试)。

有时你需要那些持久存储在DB中的工具 - 然后创建一个单独的工厂(PersistedObjectFactory),它使用原始工厂来创建对象,然后它将存储它们。

PS:在随机化之前的例子中我使用Qala Datagen(免责声明 - 我是作者)。