tutorial logo example español java junit junit4

java - logo - junit tutorial



¿Cómo puedo construir un conjunto de pruebas JUnit4 configurable? (3)

Guava tiene un extenso conjunto de pruebas para implementaciones de colecciones escritas en JUnit3 que se parecen a:

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

y luego se prueban diferentes colecciones utilizando TestSuiteBuilder s que pasan un conjunto de características y generadores para el tipo de colección, y un marco altamente reflexivo identifica el conjunto de métodos de prueba a ejecutar.

Me gustaría construir algo similar en JUnit4, pero no me queda claro cómo hacerlo: ¿construir mi propio Runner ? Teorias Mi mejor conjetura hasta ahora es escribir algo como

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 }

La pregunta general es algo así como: ¿cómo, en JUnit4, puedo crear un conjunto de pruebas para un tipo de interfaz y luego aplicar esas pruebas a implementaciones individuales?


Con respecto a si construir o no tu propio Runner ... Creo que no deberías intentar construir el tuyo de inmediato, sino parametrizar tus pruebas unitarias.

Una opción es anotar su clase con @RunWith(Parameterized.class) e insertar un método estático anotado con @Parameters que se usará para la parametrización real utilizando el constructor de la prueba JUnit. A continuación un ejemplo que tomé descaradamente de 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)); } }

Esto hará que todos sus métodos de prueba usen los mismos parámetros que usualmente se asignarán a los campos correspondientes en la clase JUnit. La clave será la creación de instancias de las diferentes implementaciones en este método estático (Dagger, Guice, fábricas, lo que sea). Luego, se pasarán automáticamente al constructor y usted será responsable de asignarlos a los campos que va a utilizar en los métodos de prueba. Como puede ver, en lugar de usar la matriz de enteros del ejemplo, solo coloque instancias de su implementación dentro. Para más información mira el enlace de arriba.

La segunda opción es usar Zohhak con la anotación @RunWith(ZohhakRunner.class) de https://github.com/piotrturski/zohhak . Esto le permitirá parametrizar sus pruebas unitarias por método en lugar de por clase. Esto podría ser más complicado con la creación de instancias de fábrica, pero se puede hacer de manera bastante elegante con un poco de trabajo. Ejemplo tomado del sitio 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) ); }

Comenzaría con la primera aproximación y si golpeas a un alimento, pasas a la segunda. Saludos y buena suerte.


En Junit puedes usar categories . Por ejemplo, esta suite ejecutará una prueba desde AllTestSuite anotada como integración:

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

También puede utilizar @ExcludeCategory . Esto es útil para eliminar pruebas lentas. Las clases de categorías son simplemente clases o interfaces antiguas de Java. Por ejemplo:

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

Solo necesitas anotar tus pruebas de acuerdo a:

@Category(Integration.class) public class MyTest{ @Test public void myTest__expectedResults(){ [...]

Una prueba podría tener más de una categoría como esta:

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

Para simplificar, normalmente creo una Suite con todas las clases en la carpeta de prueba usando la caja de herramientas de 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 {}

Esto funciona incluyendo AllTestSuite todas las clases en la misma carpeta y subcarpetas, incluso si no tienen el sufijo _Test. Pero no podrá ver las pruebas que no estén en la misma carpeta o subcarpetas. junit-toolbox está disponible en Maven con:

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

Ahora solo necesitas ejecutar la Suite que se adapte a tus necesidades :)

ACTUALIZACIÓN : En Spring se encuentra la anotación @IfProfileValue que le permite ejecutar la prueba condicionalmente como:

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

Para obtener más información, consulte Anotaciones de prueba de primavera


Puede usar las reglas de JUnit para ignorar condicionalmente las pruebas (terminarán como omitidas en el informe de Maven, pero podría haber una manera de resolver esto que no conozco).

Esto se basa en la regla en este artículo . Cambié un poco la regla, ver aquí .

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

Ejemplo de prueba para la lista de matrices:

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

Alguna lista que no admite eliminar:

public class UnmodifiableListTest extends AbstractCollectionTest { public UnmodifiableListTest() { super(Collections.unmodifiableList(new ArrayList<>())); } }