我的单元测试派生类是否应继承自我的超类测试?

时间:2014-08-07 16:45:29

标签: java unit-testing inheritance junit

我有一个单元测试(JUnit 4),它有一些非常复杂的设置:

public class HDF5CompoundDSCutoffCachingBridgeTest {
    protected final String TEST_PATH = "test-out.h5";
    protected final DatasetName TEST_DS = new DatasetName("group", "foo");
    protected WritableDataPoint testPoint1;
    protected WritableDataPoint emptyPoint;
    protected WritableDataPoint[] emptyPoints;
    protected WritableDataPoint[] fullPoints;
    protected IHDF5Writer writer;
    protected HDF5CompoundDSBridgeConfig config;


    protected HDF5CompoundDSCachingBridge<WritableDataPoint> dtBridge;

    @Before
    public void setUp() throws Exception {
        try {
            File file = new File(TEST_PATH);

            testPoint1 = new WritableDataPoint(new long[][]{{1, 2}}, new long[][]{{3, 4}}, 6, 10l);
            emptyPoint = new WritableDataPoint(new long[][]{}, new long[][]{}, 0, 0l);

            emptyPoints = new WritableDataPoint[]{emptyPoint, emptyPoint, emptyPoint, emptyPoint, emptyPoint,};
            fullPoints = new WritableDataPoint[]{testPoint1, testPoint1, testPoint1, testPoint1, testPoint1,};


            config = new HDF5CompoundDSBridgeConfig(HDF5StorageLayout.CHUNKED,
                                                    HDF5GenericStorageFeatures.MAX_DEFLATION_LEVEL,
                                                    5);

            writer = HDF5Writer.getWriter(file);
            HDF5CompoundDSBridgeBuilder<WritableDataPoint> dtBuilder =
                    new HDF5CompoundDSBridgeBuilder<>(writer, config);
            dtBuilder.setChunkSize(5);
            dtBuilder.setStartSize(5);
            dtBuilder.setTypeFromInferred(WritableDataPoint.class);
            dtBuilder.setCutoff(true);

            buildBridge(dtBuilder);

        } catch (StackOverflowError e) {
            // other stuff
        }

    }

    protected void buildBridge(HDF5CompoundDSBridgeBuilder<WritableDataPoint> dtBuilder) throws Exception {
        dtBridge = dtBuilder.buildCaching(TEST_DS);
    }

    // a bunch of tests follow
}

现在,我已经将这个正在测试的类子类化了。它仍然需要原始测试中的复杂设置。它的行为几乎相同 - 它需要完成父类所做的所有事情,并且它将稍微不同的信息传递给dtBuilder

我可以复制粘贴配置代码。这对我有点吸引力:这些实际上是两个不同的测试对象,未来它们的配置需求可能会发生变化。如果它们具有完全独立的设置代码,那么我可以对对象的工作方式进行更改,而不必担心不必要地破坏测试。但这似乎是错误的。

我所做的是继承超类的类:

public class HDF5CompoundDSAsyncBridgeTest extends HDF5CompoundDSCutoffCachingBridgeTest {
    protected HDF5CompoundDSAsyncBridge<WritableDataPoint> dtBridge;


    @Override
    protected void buildBridge(HDF5CompoundDSBridgeBuilder<WritableDataPoint> dtBuilder) throws Exception {
        dtBuilder.setAsync(true);
        dtBridge = dtBuilder.buildAsync(TEST_DS);
        assertNotNull(dtBridge);
    }
    // more tests go here
}

我喜欢这个,因为我要验证我的新类仍然通过其父类所做的所有测试,但是单元测试相互继承似乎有些不对。

这里的举动是什么?我读过这样的帖子:What does “DAMP not DRY” mean when talking about unit tests?表明继承单元测试是一个坏主意,但复制+粘贴整块代码似乎是错误的。

1 个答案:

答案 0 :(得分:2)

IMO没有本质上错误的生活在层次结构中的测试,但它不是唯一的选择,并且有任何方式存在各种各样的争论。测试倾向于重复更多代码,但对我来说,相同的设置太容易出错,胖指法等依赖于复制和粘贴,特别是如果设置经常变化足够你很可能搞砸了其中一个子类的设置。

所有这一切,为什么不从子类调用超类的设置方法,在必要时覆盖和/或扩展行为?它并不像子类一样孤立。

如果有某些原因不起作用,或者需要进行大量的子类定制,请考虑在值对象中包装这些选项/值,数据链等。

然后每个测试类可能使用相同的@Before功能,但它看起来更像:

protected TestValues testValues;

@Before
public void setUp() throws Exception {
    testValues = new TestSetupHelper();
}

如果需要对参数进行参数设置,您可以向TestSetupHelper的ctor等添加内容。