web-dev-qa-db-fra.com

Exclure un test non param de la classe de test paramétrée

Existe-t-il une annotation dans JUnit pour exclure un test non param de la classe de test paramétrée?

44
user405516

JUnit 5

À partir de Junit 5.0.0, vous pouvez maintenant annoter vos méthodes de test avec @ParameterizedTest. Donc, pas besoin de classes internes. Il existe de nombreuses manières de fournir les arguments du test paramétré en dehors de ValueSource, comme indiqué ci-dessous. Voir le guide officiel de l'utilisateur Junit pour plus de détails: 

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class ComponentTest {

    @ParameterizedTest
    @ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
    public void testCaseUsingParams(String candidate) throws Exception {
    }

    @Test
    public void testCaseWithoutParams() throws Exception {
    }
}

JUnit 4

Si vous utilisez toujours Junit 4 (j'ai testé avec v4.8.2), vous pouvez utiliser le programme d'exécution inclus avec les classes internes et le programme d'exécution paramétré:

import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Enclosed.class)
public class ComponentTest {

    @RunWith(Parameterized.class)
    public static class ComponentParamTests {

        @Parameters
        ...

        @Test
        public void testCaseUsingParams() throws Exception {
        }
    }

    public static class ComponentSingleTests {

        @Test
        public void testCaseWithoutParams() throws Exception {
        }
    }
}
46
Matthew Madson

I je viens de découvrir que l'on peut utiliser JUnitParams . J'ai converti l'un de mes tests maintenant pour l'utiliser et cela fonctionne à merveille.

12
AmanicA

La meilleure pratique consiste à déplacer ces tests non paramétrés vers une autre classe (fichier .Java).

6
Jeanne Boyarsky

Zohhak test runner est un moyen plus simple de paramétrer des tests spécifiques. Merci, Piotr!

2
AmanicA

J'ai été capable de faire quelque chose de très similaire à la réponse de Matthew Madson et j'ai trouvé utile de créer une classe de base pour encapsuler les fonctions d'installation et d'assistance communes entre les tests simples et param. Cela fonctionne sans utiliser Enclosed.class .

 @RunWith(Suite.class)
 @SuiteClasses({ComponentTest.ComponentParamTests.class, ComponentTest.ComponentSingleTests.class})
 public class ComponentTest {

    public static class TestBase {
        @Spy
        ...
        @Before
        ...
    }

    @RunWith(Parameterized.class)
    public static class ComponentParamTests extends TestBase{
        @Parameter
        ...
        @Parameters
        ...
        @Test
        ...
    }
    public static class ComponentSingleTests extends TestBase{
        @Test
        ...
    }
}
2
Phil Ninan

Il semble que TestNG ne souffre pas de ce problème . Je ne suis pas désespéré et j'ai donc modifié la classe Parameterized intégrée pour prendre en charge cette fonctionnalité. Annotez simplement les tests applicables sous la forme @NonParameterized. Notez que cette classe ne fonctionne qu'avec ses annotations, c'est-à-dire, contrôlez vos importations.

import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;
import Java.lang.reflect.Modifier;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Iterator;
import Java.util.List;

import org.junit.Test;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Suite;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;

/**
 * <p>
 * The custom runner <code>Parameterized</code> implements parameterized tests.
 * When running a parameterized test class, instances are created for the
 * cross-product of the test methods and the test data elements.
 * </p>
 * For example, to test a Fibonacci function, write:
 *
 * <pre>
 * &#064;RunWith(Parameterized.class)
 * public class FibonacciTest {
 *     &#064;Parameters
 *     public static List&lt;Object[]&gt; data() {
 *         return Arrays.asList(new Object[][] {
 *                 Fibonacci,
 *                 { {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;
 *     }
 *
 *     &#064;Test
 *     public void test() {
 *         assertEquals(fExpected, Fibonacci.compute(fInput));
 *     }
 * }
 * </pre>
 * <p>
 * Each instance of <code>FibonacciTest</code> will be constructed using the
 * two-argument constructor and the data values in the
 * <code>&#064;Parameters</code> method.
 * </p>
 */
public class Parameterized extends Suite {

    /**
     * Annotation for a method which provides parameters to be injected into the
     * test class constructor by <code>Parameterized</code>
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public static @interface Parameters {
    }

    /**
     * Annotation for a methods which should not be parameterized
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public static @interface NonParameterized {
    }

    private class TestClassRunnerForParameters extends
            BlockJUnit4ClassRunner {
        private final int fParameterSetNumber;

        private final List<Object[]> fParameterList;

        TestClassRunnerForParameters(Class<?> type,
                List<Object[]> parameterList, int i) throws InitializationError {
            super(type);
            fParameterList = parameterList;
            fParameterSetNumber = i;
        }

        @Override
        public Object createTest() throws Exception {
            return getTestClass().getOnlyConstructor().newInstance(
                    computeParams());
        }

        private Object[] computeParams() throws Exception {
            try {
                return fParameterList.get(fParameterSetNumber);
            } catch (ClassCastException e) {
                throw new Exception(String.format(
                        "%s.%s() must return a Collection of arrays.",
                        getTestClass().getName(), getParametersMethod(
                                getTestClass()).getName()));
            }
        }

        @Override
        protected String getName() {
            return String.format("[%s]", fParameterSetNumber);
        }

        @Override
        protected String testName(final FrameworkMethod method) {
            return String.format("%s[%s]", method.getName(),
                    fParameterSetNumber);
        }

        @Override
        protected void validateConstructor(List<Throwable> errors) {
            validateOnlyOneConstructor(errors);
        }

        @Override
        protected Statement classBlock(RunNotifier notifier) {
            return childrenInvoker(notifier);
        }

        @Override
        protected List<FrameworkMethod> computeTestMethods() {
            List<FrameworkMethod> ret = super.computeTestMethods();
            for (Iterator<FrameworkMethod> i = ret.iterator(); i.hasNext();) {
                FrameworkMethod frameworkMethod =
                    (FrameworkMethod) i.next();
                if (isParameterized() ^
                    !frameworkMethod.getMethod().isAnnotationPresent(
                        NonParameterized.class)) {
                    i.remove();
                }
            }
            return ret;
        }

        protected boolean isParameterized() {
            return true;
        }
    }

    private class TestClassRunnerForNonParameterized extends
        TestClassRunnerForParameters {

        TestClassRunnerForNonParameterized(Class<?> type,
            List<Object[]> parameterList, int i)
            throws InitializationError {
            super(type, parameterList, i);
        }

        protected boolean isParameterized() {
            return false;
        }
    }

    private final ArrayList<Runner> runners = new ArrayList<Runner>();

    /**
     * Only called reflectively. Do not use programmatically.
     */
    public Parameterized(Class<?> klass) throws Throwable {
        super(klass, Collections.<Runner> emptyList());
        List<Object[]> parametersList = getParametersList(getTestClass());
        if (parametersList.size() > 0) {
            try {
                runners.add(new TestClassRunnerForNonParameterized(
                    getTestClass()
                        .getJavaClass(), parametersList, 0));
            } catch (Exception e) {
                System.out.println("No non-parameterized tests.");
            }
        }
        try {
            for (int i = 0; i < parametersList.size(); i++) {
                runners.add(new TestClassRunnerForParameters(getTestClass()
                    .getJavaClass(),
                    parametersList, i));
            }
        } catch (Exception e) {
            System.out.println("No parameterized tests.");
        }
    }

    @Override
    protected List<Runner> getChildren() {
        return runners;
    }

    @SuppressWarnings("unchecked")
    private List<Object[]> getParametersList(TestClass klass)
            throws Throwable {
        return (List<Object[]>) getParametersMethod(klass).invokeExplosively(
                null);
    }

    private FrameworkMethod getParametersMethod(TestClass testClass)
            throws Exception {
        List<FrameworkMethod> methods = testClass
                .getAnnotatedMethods(Parameters.class);
        for (FrameworkMethod each : methods) {
            int modifiers = each.getMethod().getModifiers();
            if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
                return each;
        }

        throw new Exception("No public static parameters method on class "
                + testClass.getName());
    }

}

Mise à jour: J'essaie d'obtenir ce genre de chose ajoutée à Junit.

0
AmanicA

J'ai fait quelque chose de similaire à la solution de Matthew. Cependant, j'ai créé deux nouveaux fichiers Java qui étendent le fichier actuel de sorte que les ComponentSingleTests ne s'exécutent pas deux fois. De cette façon, ils peuvent partager des variables de membre communes et des méthodes d'assistance à partir de la classe parente. Le problème que j'ai eu avec la solution de Matthew était que mon test unique a été exécuté deux fois au lieu d'une fois, car la classe Enclosed.class (qui étend la classe Suite.class) effectuera votre test deux fois comme décrit dans ce lien Prevent tests junit de courir deux fois

ComponentTest.Java

public class ComponentTest {
    public int sharedMemberVariables; 
    ... 
}

ComponentParamTests.Java

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class ComponentParamTests extends ComponentTest {

    @Parameters
    ...

    @Test
    public void testCaseUsingParams() throws Exception {
    }
}

ComponentSingleTests.Java

import org.junit.Test;

public class ComponentSingleTests {

    @Test
    public void testCaseWithoutParams() throws Exception {
        ...
    }
}
0
Chee Loong Soon

En supposant que vous utilisiez Parametrized.class pour exécuter votre classe de test, marquez tous les tests non paramétrés comme @Ignored. Sinon, vous pouvez créer une classe interne statique pour regrouper tous vos tests paramétrés et un autre - pour les non paramétrés.

0
Artem Oboturov