2016-01-28 15 views
20

Guava ma bogaty zestaw testów dla wdrożeń zbiórki pisanych w JUnit3 które wyglądają jak:Jak zbudować konfigurowalny zestaw testów JUnit4?

/* 
* 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() { 
    ... 
    } 
} 

a następnie różne zbiory są testowane za pomocą TestSuiteBuilder s, które przechodzą w zestawie cech i generatorów do rodzaju zbiórki, a szkielet silnie refleksyjny identyfikuje zestaw metod testowych do uruchomienia.

Chciałbym zbudować coś podobnego w JUnit4, ale nie jest dla mnie jasne, jak sobie z tym poradzić: budowa własnego Runner? Teorie? Mój najlepszy przypuszczenie tak daleko jest napisać coś podobnego

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 
} 

Ogólne pytanie jest coś takiego: Jak w JUnit4, mogę zbudować zestaw testów dla typu interfejsu, a następnie zastosować te testy do indywidualnych wdrożeń?

+0

Podoba mi się Twoje podejście, utrzymywanie klasy testowej w prostocie. Klasa @Category lub Sparametryzowana również tutaj nie pasuje. Ponieważ problem polega na odkryciu, czy metoda jest implementowana przez jego implementację, co można zrobić poprzez refleksję tylko wtedy, gdy kod implementacyjny ma jakąś demarkację, inne proste podejście to takie, które zrobiłeś. Klasa testowa określa (poprzez cechy), które metody mają być testowane. Aby go jeszcze bardziej uprościć, możesz eksplorować Dataproviders i może nie potrzebować wielu konkretnych klas testowych, takich jak MyListImplTest i Dataprovider. HTH –

Odpowiedz

4

W JUnit można użyć categories. Na przykład ten pakiet będzie wykonać próbę al z AllTestSuite odnotowany jako integracji:

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 {} 

Można również użyć @ExcludeCategory. Jest to przydatne do usuwania powolnych testów. Kategorie classes to po prostu stare klasy lub interfejsy Java. Na przykład:

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

Wystarczy tylko anotate swoje testy acordingly:

@Category(Integration.class) 
public class MyTest{ 

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

Jedno badanie może mieć więcej niż jedną kategorię tak:

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

Dla uproszczenia zazwyczaj tworzę Suite ze wszystkimi klasami w folderze testowym przy użyciu zestawu narzędzi Google:

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 {} 

Działa to w ramach AllTestSuite wszystkich klas w tym samym folderze i podfolderach, nawet jeśli nie mają one sufiksu _Test. Ale nie będzie w stanie zobaczyć testu, który nie znajduje się w tym samym folderze lub podfolderach.junit-toolbox jest dostępny w Maven z:

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

Teraz wystarczy tylko wykonać Suite, który odpowiada Twoim potrzebom :)

UPDATE: Wiosną nie jest @IfProfileValue adnotacji, który pozwala na wykonanie testu warunkowo jak:

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

więcej informacji można znaleźć Spring JUnit Testing Annotations

1

Możesz użyć reguł JUnit, aby warunkowo zignorować testy (będą one kończyły się jako pomijane w raporcie maven'a, ale może być sposób na to, żeby się w to wtrącać, o czym nie wiem). Jest to oparte na regule in this article. Zmieniłem nieco regułę, 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); 
    } 
} 

Przykład testowy dla listy tablicy:

public class ArrayListTest extends AbstractCollectionTest { 

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

} 

Niektóre lista, która nie obsługuje usunięcia:

public class UnmodifiableListTest extends AbstractCollectionTest { 

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

Co do tego, czy zbudować własnego Runnera ... Myślę, że nie powinieneś natychmiast próbować tworzyć własnych, ale zamiast tego sparametryzować testy jednostkowe.

Jedną z opcji jest opisanie klasy za pomocą @RunWith(Parameterized.class) i wstawienie statycznej metody z adnotacją @Parameters, która zostanie użyta do faktycznej parametryzacji za pomocą konstruktora testu JUnit. Poniżej przykład ja bezwstydnie wziął od https://github.com/junit-team/junit/wiki/Parameterized-tests:

@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)); 
    } 
} 

ta będzie mieć wszystkie metody badań używać tych samych parametrów jak zwykle będą przypisane do odpowiednich pól w klasie JUnit. Kluczem będzie wprowadzenie różnych implementacji w tej statycznej metodzie (Sztylet, Guice, fabryki, cokolwiek). Następnie zostaną one automatycznie przekazane do konstruktora, a Ty będziesz odpowiedzialny za przypisanie ich do pól, których będziesz używać w metodach testowania. Jak widzisz, zamiast używać przykładowej tablicy liczb całkowitych, po prostu umieść wewnątrz instancje swojej implementacji. Aby uzyskać więcej informacji, spójrz na powyższy link.

Drugą opcją jest użycie Zohhak z adnotacją @RunWith(ZohhakRunner.class) z https://github.com/piotrturski/zohhak. Umożliwi to sparametryzowanie testów jednostkowych dla każdej metody zamiast dla każdej klasy. Może to być utrudnione z instancją fabularną, ale można to zrobić całkiem elegancko z odrobiną pracy. Przykład wzięty z witryny Zohhak:

@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) ); 
} 

Chciałbym zacząć od pierwszego podejścia i jeśli trafisz alimit, przejść do drugiego. Pozdrowienia i powodzenia.

Powiązane problemy