如何编写接口的junittesting?

为接口编写junittesting的最好方法是什么,以便它们可以用于具体的实现类?

你有这个接口和实现类:

public interface MyInterface { /** Return the given value. */ public boolean myMethod(boolean retVal); } public class MyClass1 implements MyInterface { public boolean myMethod(boolean retVal) { return retVal; } } public class MyClass2 implements MyInterface { public boolean myMethod(boolean retVal) { return retVal; } } 

你将如何写一个testing对接口,所以你可以使用它的类?

可能性1:

 public abstract class MyInterfaceTest { public abstract MyInterface createInstance(); @Test public final void testMyMethod_True() { MyInterface instance = createInstance(); assertTrue(instance.myMethod(true)); } @Test public final void testMyMethod_False() { MyInterface instance = createInstance(); assertFalse(instance.myMethod(false)); } } public class MyClass1Test extends MyInterfaceTest { public MyInterface createInstance() { return new MyClass1(); } } public class MyClass2Test extends MyInterfaceTest { public MyInterface createInstance() { return new MyClass2(); } } 

优点:

  • 只需要一种方法来实施

缺点:

  • 被testing的类的依赖性和模拟对象对所有的testing都是相同的

可能性2:

 public abstract class MyInterfaceTest public void testMyMethod_True(MyInterface instance) { assertTrue(instance.myMethod(true)); } public void testMyMethod_False(MyInterface instance) { assertFalse(instance.myMethod(false)); } } public class MyClass1Test extends MyInterfaceTest { @Test public void testMyMethod_True() { MyClass1 instance = new MyClass1(); super.testMyMethod_True(instance); } @Test public void testMyMethod_False() { MyClass1 instance = new MyClass1(); super.testMyMethod_False(instance); } } public class MyClass2Test extends MyInterfaceTest { @Test public void testMyMethod_True() { MyClass1 instance = new MyClass2(); super.testMyMethod_True(instance); } @Test public void testMyMethod_False() { MyClass1 instance = new MyClass2(); super.testMyMethod_False(instance); } } 

优点:

  • 每个testing都包含依赖关系和模拟对象

缺点:

  • 每个实现testing类都需要编写额外的testing方法

你更喜欢哪种可能性或者你使用了什么其他的方式?

与@dlev给出的投票赞成的答案相反,有时候写一个像你所build议的那样的testing是非常有用的/需要的。 通过其接口expression的一个类的公共API是最重要的testing。 这就是说,我不会使用您提到的方法,而是使用参数化testing,而参数是要testing的实现:

 @RunWith(Parameterized.class) public class InterfaceTesting { public MyInterface myInterface; public InterfaceTesting(MyInterface myInterface) { this.myInterface = myInterface; } @Test public final void testMyMethod_True() { assertTrue(myInterface.myMethod(true)); } @Test public final void testMyMethod_False() { assertFalse(myInterface.myMethod(false)); } @Parameterized.Parameters public static Collection<Object[]> instancesToTest() { return Arrays.asList( new Object[]{new MyClass1()}, new Object[]{new MyClass2()} ); } } 

我强烈反对@dlev。 通常,编写使用接口的testing是非常好的做法。 接口定义了客户端和实现之间的契约。 通常你所有的实现都必须通过完全相同的testing。 显然,每个实现都可以有自己的testing。

所以,我知道2个解决scheme。

  1. 用各种使用接口的testing来实现抽象testing用例。 声明返回具体实例的抽象保护方法。 现在inheritance这个抽象类,根据需要为每个接口实现多次,并相应地实现所提到的工厂方法。 您也可以在这里添加更具体的testing。

  2. 使用testing套件 。

我也不同意dlev,没有什么错误的写你的testing对接口,而不是具体的实现。

您可能想要使用参数化testing。 下面是使用TestNG的样子,它更像JUnit(因为你不能直接将parameter passing给testing函数):

 @DataProvider public Object[][] dp() { return new Object[][] { new Object[] { new MyImpl1() }, new Object[] { new MyImpl2() }, } } @Test(dataProvider = "dp") pubic void f(MyInterface itf) { // will be called, with a different implementation each time } 

我通常会避免写一个接口的unit testing,简单的理由是,一个接口,不pipe你喜欢它, 没有定义function 。 它以语法要求来安装它的实现者,但就是这样。

相反,unit testing旨在确保您所期望的function存在于给定的代码path中。

这就是说,有些情况下这种types的testing是有道理的。 假设你想要这些testing,以确保你写的类(共享给定的接口),事实上,共享相同的function,那么我宁愿你的第一个select。 它使实现子类中最容易注入testing过程。 另外,我不认为你的“con”是真的。 没有理由不能让实际上被testing的类提供自己的模拟(尽pipe我认为如果你真的需要不同的模拟,那么表明你的接口testing不是统一的)。

除了主题之外,还分享了更新的解决scheme见解

我也在寻找一种正确有效的方法来testing(基于JUnit)多个接口和抽象类的实现的正确性。 不幸的是,JUnit的@Parameterizedtesting和TestNG的等价概念都不能正确地符合我的要求,因为我不知道可能存在的这些接口/抽象类的实现列表。 也就是说,可能会开发新的实现,testing人员可能无法访问所有现有的实现; 因此testing类指定实现类的列表是没有效率的。

在这一点上,我发现了以下项目似乎提供了一个完整和高效的解决scheme,以简化这种types的testing: https : //github.com/Claudenw/junit-contracts 。 它基本上允许通过合同testing类中的注解@Contract(InterfaceClass.class)来定义“合同testing”。 然后,实现者将创build一个实现特定的testing类,注解@RunWith(ContractSuite.class)@ContractImpl(value = ImplementationClass.class) ; 引擎将自动应用适用于ImplementationClass的任何契约testing,方法是查找为ImplementationClass派生的任何接口或抽象类定义的所有契约testing。 我还没有testing过这个解决scheme,但这听起来很有希望。

我也find了以下库: http : //www.jqno.nl/equalsverifier/ 。 这个满足了一个类似的需求,这个需求是专门针对Object.equals和Object.hashcode契约的。

同样, https: //bitbucket.org/chas678/testhelpers/src演示validation一些Java fondamental契约的策略,包括Object.equals,Object.hashcode,Comparable.compare,Serializable。 这个项目使用简单的testing结构,我相信它可以很容易地复制,以满足任何特定的需求。

那么,现在呢? 我会保持这个职位更新与其他有用的信息,我可能会发现。

我不认为“抽象”的关键字是必要的接口了。 看到这个问题: Java抽象接口