2016-01-28 7 views
20

Guava hat eine umfangreiche Reihe von Tests für die Sammlung Implementierungen in JUnit3 geschrieben, die wie folgt aussehen:Wie kann ich eine konfigurierbare JUnit4-Testsuite erstellen?

/* 
* Copyright (C) 2008 The Guava Authors 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
public class CollectionRemoveTester<E> extends AbstractTester<E> { 

    @CollectionFeature.Require(SUPPORTS_REMOVE) 
    @CollectionSize.Require(absent = ZERO) 
    public void testRemove_present() { 
    ... 
    } 
} 

und dann unter Verwendung TestSuiteBuilder s verschiedenen Sammlungen getestet, die in einer Reihe von Merkmalen und Generatoren für die Sammlung Art passieren, und ein stark reflektierendes Framework identifiziert die Testmethoden, die ausgeführt werden sollen.

Ich möchte etwas ähnliches in JUnit4 bauen, aber es ist mir nicht klar, wie ich vorgehen soll: meine eigene Runner zu bauen? Theorien? Meine beste Vermutung ist, so weit, etwas zu schreiben, wie

abstract class AbstractCollectionTest<E> { 
    abstract Collection<E> create(E... elements); 
    abstract Set<Feature> features(); 

    @Test 
    public void removePresentValue() { 
     Assume.assumeTrue(features().contains(SUPPORTS_REMOVE)); 
     ... 
    } 
} 

@RunWith(JUnit4.class) 
class MyListImplTest<E> extends AbstractCollectionTest<E> { 
    // fill in abstract methods 
} 

Die allgemeine Frage ist so etwas wie: wie, in JUnit4, könnte ich eine Reihe von Tests für einen Schnittstelle Typen bauen, und dann diese Tests auf einzelne Implementierungen anwenden?

+0

Ich mag Ihren Ansatz, es hält die Testklasse einfach. @ Kategorie oder parametrisierte Klasse passt auch nicht hier. Da das Problem darin liegt, herauszufinden, ob eine Methode durch ihre Implementierung implementiert wird, was nur durch Reflektion geschehen kann, wenn der implementierende Code eine Abgrenzung hat, ist der andere einfache Ansatz einer, den Sie getan haben. Die Testklasse definiert (über Features) welche Methoden getestet werden sollen. Um es weiter zu vereinfachen, können Sie Dataprovider erkunden und benötigen möglicherweise nicht die mehreren konkreten Testklassen, wie MyListImplTest und ein Datenprovider. HTH –

Antwort

4

In Junit können Sie categories verwenden. Zum Beispiel wird diese Suite al-Test von der AllTestSuite kommentierte die Integration auszuführen:

import org.junit.experimental.categories.Categories; 
import org.junit.experimental.categories.Categories.IncludeCategory; 
import org.junit.runner.RunWith; 
import org.junit.runners.Suite; 

@RunWith(Categories.class) 
@IncludeCategory(Integration.class) 
@Suite.SuiteClasses ({AllTestsSuite.class}) 
public class IntegrationTestSuite {} 

Sie auch @ExcludeCategory verwenden können. Dies ist nützlich, um langsame Tests zu entfernen. Kategorien-Klassen sind einfach alte Java-Klassen oder -Schnittstellen. Zum Beispiel:

public interface Integration{} 
public interface Performance{} 
public interface Slow{} 
public interface Database{} 

Sie müssen nur Ihre Tests anotate acordingly:

@Category(Integration.class) 
public class MyTest{ 

    @Test 
    public void myTest__expectedResults(){ 
    [...] 

Ein Test könnte mehr als eine Kategorie wie dieses:

@Category({Integration.class,Database.class}) 
    public class MyDAOTest{ 

Der Einfachheit halber ich eine Regel erstellen Suite mit allen Klassen im Testordner mit google toolbox:

import org.junit.runner.RunWith; 

import com.googlecode.junittoolbox.ParallelSuite; 
import com.googlecode.junittoolbox.SuiteClasses; 

@RunWith(ParallelSuite.class) 
@SuiteClasses({"**/**.class",   //All classes 
      enter code here "!**/**Suite.class" }) //Excepts suites 
public class AllTestsSuite {} 

Dies funktioniert, einschließlich AllTestSuite alle Klassen in den gleichen Ordner und Unterordner, auch wenn sie nicht _Test Sufix haben. Sie können jedoch keine Tests sehen, die sich nicht im selben Ordner oder Unterordner befinden.junit-toolbox ist in Maven mit:

<dependency> 
    <groupId>com.googlecode.junit-toolbox</groupId> 
    <artifactId>junit-toolbox</artifactId> 
    <version>2.2</version> 
</dependency> 

Jetzt müssen Sie nur die Suite auszuführen, die Ihren Bedürfnissen entspricht :)

UPDATE: Im Frühjahr gibt es die @IfProfileValue Anmerkung, die Sie Test ausführen können bedingt wie:

@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) 
@Test 
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() { 

weitere Informationen finden Sie Spring JUnit Testing Annotations

1

Sie können JUnit-Regeln verwenden, um Tests bedingungslos zu ignorieren (sie werden im maven-Bericht als übersprungen angezeigt, aber es könnte einen Weg geben, um das herumzutüfteln, von dem ich nichts weiß).

Dies basiert auf Regel in this article. Ich habe die Regel ein wenig geändert, see here.

public abstract class AbstractCollectionTest { 

@Rule 
public ConditionalSupportRule rule = new ConditionalSupportRule(); 

private Collection<String> collection; 
private Set<Class<? extends Feature>> features; 

public AbstractCollectionTest(Collection<String> collection, 
           Class<? extends Feature> ... features) { 
    this.collection = collection; 

    this.features = new HashSet<>(); 
    for (Class<? extends Feature> feature : features) { 
     this.features.add(feature); 
    } 
} 

@Test 
@ConditionalSupport(condition = SupportsRemove.class) 
public void remove() throws Exception { 

    // ... 
    System.out.println("test run"); 
} 

private interface Feature {} 

public class SupportsRemove implements RunCondition, Feature { 

    public SupportsRemove() { 
    } 

    @Override 
    public boolean isSatisfied() { 
     return features.contains(SupportsRemove.class); 
    } 
} 

Beispiel Test für Array-Liste:

public class ArrayListTest extends AbstractCollectionTest { 

    public ArrayListTest() { 
     super(new ArrayList<>(), SupportsRemove.class); 
    } 

} 

Einige Liste, die nicht entfernt werden unterstützt:

public class UnmodifiableListTest extends AbstractCollectionTest { 

    public UnmodifiableListTest() { 
     super(Collections.unmodifiableList(new ArrayList<>())); 
    } 
} 
2

In Bezug darauf, ob Sie Ihren eigenen Runner bauen oder nicht ... Ich denke, Sie sollten nicht sofort versuchen, Ihre eigenen Runner zu bauen, sondern stattdessen Ihre Unit-Tests parametrisieren.

Eine Möglichkeit ist, die Klasse mit @RunWith(Parameterized.class) und legen Sie eine statische Methode mit @Parameters kommentierte mit Anmerkungen zu versehen, die für die eigentliche Parametrierung verwendet wird, um den Konstruktor des JUnit-Tests. Im Folgenden ein Beispiel aus ich schamlos https://github.com/junit-team/junit/wiki/Parameterized-tests nahm:

@RunWith(Parameterized.class) 
public class FibonacciTest { 
    @Parameters 
    public static Collection<Object[]> data() { 
     return Arrays.asList(new Object[][] {  
       { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } 
      }); 
    } 

    private int fInput; 

    private int fExpected; 

    public FibonacciTest(int input, int expected) { 
     fInput= input; 
     fExpected= expected; 
    } 

    @Test 
    public void test() { 
     assertEquals(fExpected, Fibonacci.compute(fInput)); 
    } 
} 

Dies wird alle Ihre Testmethoden die gleichen Parameter verwenden, wie sie in der Regel zu den entsprechenden Feldern in der JUnit-Klasse zugeordnet werden. Der Schlüssel wird die Instanziierung der verschiedenen Implementierungen in dieser statischen Methode sein (Dolch, Guice, Fabriken, was auch immer). Sie werden dann automatisch an den Konstruktor übergeben und Sie sind verantwortlich für die Zuordnung zu den Feldern, die Sie in den Testmethoden verwenden werden. Wie Sie sehen, platzieren Sie einfach die Instanzen Ihrer Implementierung, statt das Ganzzahl-Array des Beispiels zu verwenden. Weitere Informationen finden Sie unter dem obigen Link.

Die zweite Option ist die Verwendung von Zohhak mit der Anmerkung @RunWith(ZohhakRunner.class) von https://github.com/piotrturski/zohhak. Dadurch können Sie Ihre Komponententests pro Methode anstatt pro Klasse parametrisieren. Dies könnte mit der Instanziierung in der Fabrik schwieriger sein, aber es kann mit ein wenig Arbeit ziemlich elegant gemacht werden. Beispiel von der Website Zohhak genommen:

@TestWith({ 
    "clerk,  45'000 USD, GOLD", 
    "supervisor, 60'000 GBP, PLATINUM" 
}) 
public void canAcceptDebit(Employee employee, Money money, ClientType clientType) { 
    assertTrue( employee.canAcceptDebit(money, clientType) ); 
} 

ich mit dem ersten Ansatz beginnen würde, und wenn Sie alimit getroffen, auf den zweiten bewegen. Prost und viel Glück.

Verwandte Themen