为一个接口的多个实现编写一个unit testing

我有一个接口List其实现包括单链接列表,双向,循环等。我为Singly编写的unit testing应该对双向以及循环和其他任何新的接口实现中的大部分都有好处。 因此,不要为每个实现重复unit testing,JUnit是否提供了一些内置的东西,让我有一个JUnittesting,并针对不同的实现运行它?

使用JUnit参数化testing我可以提供不同的实现,如Singly,double,circular等,但是对于每个实现,同一个对象用于执行类中的所有testing。

使用JUnit 4.0+,您可以使用参数化testing :

  • @RunWith(value = Parameterized.class)注释添加到您的testing夹具
  • 创build一个返回Collectionpublic static方法,用@Parameters对其进行@Parameters ,并将SinglyLinkedList.classDoublyLinkedList.classCircularList.class等放入该集合中
  • 添加一个构造函数到您的testing夹具需要Classpublic MyListTest(Class cl) ,并将Class存储在一个实例variableslistClass
  • setUp方法或@Before ,使用List testList = (List)listClass.newInstance();

通过上面的设置,参数化的runner将为您在@Parameters方法中提供的每个子类创build一个testing夹具MyListTest的新实例,让您为每个需要testing的子类执行相同的testing逻辑。

我可能会避免JUnit的参数化testing(恕我直言,这是非常笨拙的实现),只是做一个抽象的Listtesting类,可以通过testing实现inheritance:

 public abstract class ListTestBase<T extends List> { private T instance; protected abstract T createInstance(); @Before public void setUp() { instance = createInstance(); } @Test public void testOneThing(){ /* ... */ } @Test public void testAnotherThing(){ /* ... */ } } 

不同的实现然后得到他们自己的具体类:

 class SinglyLinkedListTest extends ListTestBase<SinglyLinkedList> { @Override protected SinglyLinkedList createInstance(){ return new SinglyLinkedList(); } } class DoublyLinkedListTest extends ListTestBase<DoublyLinkedList> { @Override protected DoublyLinkedList createInstance(){ return new DoublyLinkedList(); } } 

这样做的好处(而不是让一个testing所有实现的testing类)是,如果有一些特定的angular落情况,你想testing一个实现,你可以添加更多的testing到特定的testing子类。

基于@dasblinkenlight和这个 anwser的驱动程序,我想出了一个我想要分享的用例的实现。

对于实现接口IImporterService类,我使用ServiceProviderPattern ( 差异API和SPI )。 如果开发了一个新的接口实现,则只需要修改META-INF / services /中的configuration文件来注册实现。

META-INF / services /中的文件以服务接口( IImporterService )的全限定类名称命名,例如

de.myapp.importer.IImporterService

这个文件包含实现IImporterService的casses列表,例如

de.myapp.importer.impl.OfficeOpenXMLImporter

工厂类ImporterFactory为客户端提供了接口的具体实现。


ImporterFactory返回接口的所有实现的列表,通过ServiceProviderPattern注册。 setUp()方法确保为每个testing用例使用新的实例。

 @RunWith(Parameterized.class) public class IImporterServiceTest { public IImporterService service; public IImporterServiceTest(IImporterService service) { this.service = service; } @Parameters public static List<IImporterService> instancesToTest() { return ImporterFactory.INSTANCE.getImplementations(); } @Before public void setUp() throws Exception { this.service = this.service.getClass().newInstance(); } @Test public void testRead() { } } 

ImporterFactory.INSTANCE.getImplementations()方法如下所示:

 public List<IImporterService> getImplementations() { return (List<IImporterService>) GenericServiceLoader.INSTANCE.locateAll(IImporterService.class); } 

你实际上可以在你的testing类中创build一个帮助器方法,将你的testingList设置为依赖于参数的一个实现的一个实例。 与此相结合,你应该能够得到你想要的行为。

扩展第一个答案,JUnit4的参数方面工作得很好。 这是我在项目testingfilter中使用的实际代码。 该类是使用工厂函数( getPluginIO )创build的,函数getPluginsNamed使用SezPoz和注释获取具有名称的所有PluginInfo类,以允许自动检测新类。

 @RunWith(value=Parameterized.class) public class FilterTests { @Parameters public static Collection<PluginInfo[]> getPlugins() { List<PluginInfo> possibleClasses=PluginManager.getPluginsNamed("Filter"); return wrapCollection(possibleClasses); } final protected PluginInfo pluginId; final IOPlugin CFilter; public FilterTests(final PluginInfo pluginToUse) { System.out.println("Using Plugin:"+pluginToUse); pluginId=pluginToUse; // save plugin settings CFilter=PluginManager.getPluginIO(pluginId); // create an instance using the factory } //.... the tests to run 

注意它是重要的(我个人不知道为什么它以这种方式工作)将集合作为实际参数数组的集合提供给构造函数,在这种情况下是一个名为PluginInfo的类。 wrapCollection静态函数执行此任务。

 /** * Wrap a collection into a collection of arrays which is useful for parameterization in junit testing * @param inCollection input collection * @return wrapped collection */ public static <T> Collection<T[]> wrapCollection(Collection<T> inCollection) { final List<T[]> out=new ArrayList<T[]>(); for(T curObj : inCollection) { T[] arr = (T[])new Object[1]; arr[0]=curObj; out.add(arr); } return out; }