为一个接口的多个实现编写一个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一个返回
Collection
的public static
方法,用@Parameters
对其进行@Parameters
,并将SinglyLinkedList.class
,DoublyLinkedList.class
,CircularList.class
等放入该集合中 - 添加一个构造函数到您的testing夹具需要
Class
:public MyListTest(Class cl)
,并将Class
存储在一个实例variableslistClass
- 在
setUp
方法或@Before
,使用List testList = (List)listClass.newInstance();
通过上面的设置,参数化的runner将为您在@Parameters
方法中提供的每个子类创build一个testing夹具MyListTest
的新实例,让您为每个需要testing的子类执行相同的testing逻辑。
我可能会避免JUnit的参数化testing(恕我直言,这是非常笨拙的实现),只是做一个抽象的List
testing类,可以通过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; }