考虑以下Java中的简单类层次结构
class Foo {
protected void someMethod(Bar bar) {
...
}
protected void someOtherMethod(Baz baz) {
...
}
}
class EnhancedFoo extends Foo {
@Override
protected void someMethod(Bar bar) {
...
}
}
我现在开始为这两个类编写JUnit单元测试。由于两个类的方法someMethod
的协定相同,因此两个类基本上需要完全相同的测试方法(关于someMethod
方法),这导致代码重复。对具有多个覆盖方法的更丰富的类层次结构执行此操作,就感觉继承对可测试性不利。
此外,即使someOtherMethod
中的方法ExtendedFoo
没有被覆盖,我也需要包含ExtendedFoo
的相关测试,因为这仍然是合同,并且是扩展的类和单元测试应该测试一下。
在Java中是否还有其他组织层次结构的方法更适合可测试性?是否有一些JUnit构造可以帮助缓解此问题?
答案 0 :(得分:3)
正如我在评论中提到的那样,我认为测试应与实现分离,并应“用例驱动”。看起来像这样:
interface Foo {
public void doSomething(...);
}
class DefaultFoo implements Foo { ... }
class EnhancedFoo extends DefaultFoo { ... }
class MyUseCaseTest {
private Foo foo = new DefaultFoo(...);
@Test
public void someRequirement() { ... }
}
class AnotherUseCaseTest {
private Foo foo = new EnhancedFoo(...);
@Test
public void differentRequirement() { ... }
}
最好是摆脱继承,但这是一个不同的话题...
答案 1 :(得分:1)
在非常相似的情况下,我们使用的一种方法是也重用est类:
class FooTest {
@Test
public void testSomeMethodBar() {
...
}
@Test
public void void someOtherMethodBaz(Baz baz) {
...
}
}
并将其扩展为子类测试:
class EnhancedFooTest extends FooTest {
@Test
public void testSomeMethodBar() {
...
}
}
JUnit
将运行此特定的覆盖测试方法,以及FooTest
中的其他默认测试。这样就消除了不必要的重复。
在适当情况下,某些测试类甚至被声明为abstract
,并通过具体测试类(具体类的测试)进行扩展。
答案 2 :(得分:0)
引用您的问题和评论
据我了解,单元测试假定类和它们的方法 做黑匣子并测试他们的合同。
还有
由于方法someMethod的合同对于两个 类,我基本上需要完全相同的测试方法(关于 这两个类的someMethod方法),从而导致代码 重复。
在通用单元测试概念的上下文中,这都是错误的假设,在 TDD 的非常狭窄的上下文中,这可能是正确的假设。您尚未为TDD标记问题,所以我只是在猜测,而TDD就是关于可以接受的的内容,从代码角度来看并不一定非常可靠。
TDD 永远不会停止开发人员编写更全面的单元测试,以满足他/她的需求,而不仅仅是合同。
您必须了解,单元测试是用于确保方法准确性的开发人员工具,并且不将方法视为黑匣子-应该测试合同以及实现细节(代码覆盖率)。 开发人员不应像琐碎的方法(如setter / getter方法)那样盲目编写单元测试。
详细介绍代码覆盖率时,您将发现必须为覆盖所有场景的目标方法编写多种测试方法。
如果以下方法的实现未在类EnhancedFoo
中更改,为什么要为此编写单元测试?应该假设父类测试将覆盖它。
protected void someMethod(Bar bar) {
...
}
您为要更改或添加的方法编写单元测试,不必担心继承层次结构。
我只是想强调 unit :)
一词的重要性