Singly Linked List、Douly、Circularなどの実装を含むインターフェイスList
があります。Singly向けに作成したユニットテストは、ほとんどのDoublyだけでなく、Circularおよびその他の新しいインターフェイスの実装にも適しています。では、実装ごとに単体テストを繰り返す代わりに、JUnitには、1つのJUnitテストを実行して別の実装に対して実行できる組み込みの機能がありますか?
JUnitのパラメーター化されたテストを使用して、Singly、double、circularなどの異なる実装を提供できますが、実装ごとに同じオブジェクトを使用してクラス内のすべてのテストを実行します。
私はおそらくJUnitのパラメータ化されたテスト(IMHOはかなり不器用に実装されています)を避け、テスト実装によって継承できる抽象List
テストクラスを作成します。
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();
}
}
(すべての実装をテストする1つのテストクラスを作成する代わりに)この方法で行うことの良い点は、1つの実装でテストする特定のコーナーケースがある場合、特定のテストサブクラスにさらにテストを追加できることです。 。
私はこれが古いことを知っていますが、@Parameter
をフィールドメンバーに入力して、値を注入します。
私の意見では、それは少しきれいです。
@RunWith(Parameterized.class)
public class MyTest{
private ThingToTest subject;
@Parameter
public Class clazz;
@Parameters(name = "{index}: Impl Class: {0}")
public static Collection classes(){
List<Object[]> implementations = new ArrayList<>();
implementations.add(new Object[]{ImplementationOne.class});
implementations.add(new Object[]{ImplementationTwo.class});
return implementations;
}
@Before
public void setUp() throws Exception {
subject = (ThingToTest) clazz.getConstructor().newInstance();
}
@ dasblinkenlight および this anwserのanwserに基づいて、共有したいユースケースの実装を思いつきました。
インターフェイスIImporterService
を実装するクラスに ServiceProviderPattern ( difference APIとSPI )を使用します。インターフェースの新しい実装が開発された場合、実装を登録するためにMETA-INF/services /の構成ファイルのみを変更する必要があります。
META-INF/services /内のファイルは、サービスインターフェイスの完全修飾クラス名(IImporterService
)にちなんで命名されます。
de.myapp.importer.IImporterService
このファイルには、IImporterService
を実装するcassesのリストが含まれています。
de.myapp.importer.impl.OfficeOpenXMLImporter
ファクトリクラスImporterFactory
は、クライアントにインターフェイスの具体的な実装を提供します。
ImporterFactory
は、 ServiceProviderPattern を介して登録されたインターフェイスのすべての実装のリストを返します。 setUp()
メソッドは、各テストケースで新しいインスタンスが使用されるようにします。
_@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);
}
_
テストList
を引数に依存する実装の1つのインスタンスに設定するヘルパークラスをテストクラスに実際に作成できます。 this と組み合わせて、あなたが望む振る舞いを得ることができるはずです。
最初の答えを拡張すると、JUnit4のパラメーターの側面は非常にうまく機能します。以下は、フィルターをテストするプロジェクトで使用した実際のコードです。クラスはファクトリー関数(getPluginIO
)を使用して作成され、関数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;
}