如果我有一个抽象类,如何避免重复测试

时间:2015-03-24 09:46:30

标签: unit-testing testing tdd

假设我正在写汽车课。除了一些其他方法之外,它应该有方法configEngine和currentGasolineConsumption。因此,我将汽油消耗的计算重构为Engine类,并使用多态来获得当前的汽油消耗:

class AbstractEngine()
{
    public:
        virtual int calculateGasolineConsumption()
        {
            //... do calculation ...
            return consumption;
        }

        // some other (pure virtual) methodes
};

class EngineA() : public AbstractEngine
{
    public:                              
        // implementation of the pure virtual methodes
};

class EngineB() : public AbstractEngine
{
    public:                              
        // implementation of the pure virtual methodes
};

class EngineC() : public AbstractEngine
{
    public:                              
        // implementation of the pure virtual methodes
        int calculateGasolineConsumption() override 
        {
             //... do new calculation ...
             return consumption;                                 
        }
};
enum EngineType {
    ENGINE_A,
    ENGINE_B,
    ENGINE_C,
};

void configEngine(EngineType engineType)
{
    m_engine = m_engineFactory.create(engineType);
}

int currentGasolineConsumption()
{
    return m_engine.calculateGasolineConsumption();
}

现在我的问题是如何在不进行单元测试重复的情况下对其进行单元测试?

如果我编写三个单元测试,对于configEngine(ENGINE_A)和configEngine(ENGINE_B)将基本上测试抽象超类的相同代码,我不喜欢这种重复。

struct EngineSpec {
    EngineType engineType;
    int expectedValue;
};

INSTANTIATE_TEST_CASE_P(, tst_car, ::testing::Values(
    EngineSpec { ENGINE_A, 3 },
    EngineSpec { ENGINE_B, 3 },
    EngineSpec { ENGINE_C, 7 }
));

TEST_F(tst_car,
       currentGasolineConsumption_configWithEngine_expectedBehaviour)
{
    EngineSpec engineSpec = GetParam();

    //Arrange
    m_car.configEngine(engineSpec.engineType);

    //Act
    auto result = m_car.currentGasolineConsumption();

    //Assert
    EXPECT_EQ(engineSpec.expectedValue, result);
}

当然只有一个重复/不必要的单元测试,但这只是一个最小的例子。在我的实际代码中,单元测试重复的数量会爆炸。

另外一件事:我不想将Engine类移到'module'之外并使用依赖注入,因为我认为这种'内部Engine类'方法更容易为客户端处理。所以客户端只有一个接口和一些枚举来使用这个模块。我想将Engine类视为实现细节。

1 个答案:

答案 0 :(得分:0)

理想情况下测试应该尽可能少地了解实现,因为当抽象不再适用时,或者是大型复杂继承链的一部分(例如,10年后)当你得到一个混合动力引擎时会发生什么?)现在似乎付出很多努力的测试仍然可以完美地运行。

但是,如果您想要务实并且不介意将测试与实现稍微联系起来,则可以从每个子项的测试用例中提取一个testGasolineConsumption(AbstractEngine engine)方法。这将检查实现是否正常工作以及基类行为是否未被覆盖。