我有一个单元测试(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?表明继承单元测试是一个坏主意,但复制+粘贴整块代码似乎是错误的。
答案 0 :(得分:2)
IMO没有本质上错误的生活在层次结构中的测试,但它不是唯一的选择,并且有任何方式存在各种各样的争论。测试做倾向于重复更多代码,但对我来说,相同的设置太容易出错,胖指法等依赖于复制和粘贴,特别是如果设置经常变化足够你很可能搞砸了其中一个子类的设置。
所有这一切,为什么不从子类调用超类的设置方法,在必要时覆盖和/或扩展行为?它并不像子类一样孤立。
如果有某些原因不起作用,或者需要进行大量的子类定制,请考虑在值对象中包装这些选项/值,数据链等。
然后每个测试类可能使用相同的@Before
功能,但它看起来更像:
protected TestValues testValues;
@Before
public void setUp() throws Exception {
testValues = new TestSetupHelper();
}
如果需要对参数进行参数设置,您可以向TestSetupHelper
的ctor等添加内容。