java 在参数化测试类中排除非参数测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3361239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 01:29:37  来源:igfitidea点击:

Excluding a non param test in parameterized test class

javajunitparameterized

提问by user405516

Is there any annotation in JUnit to exclude a non param test in parameterized test class?

JUnit 中是否有任何注释可以排除参数化测试类中的非参数测试?

回答by Matthew Madson

JUnit 5

JUnit 5

As of Junit 5.0.0 you can now annotate your test methods with @ParameterizedTest. So no need for inner classes. There are many ways to supply the arguments to the parameterized test apart from ValueSource as shown below. See the official junit user guidefor details:

从 Junit 5.0.0 开始,您现在可以使用@ParameterizedTest. 所以不需要内部类。除了 ValueSource 之外,还有很多方法可以为参数化测试提供参数,如下所示。有关详细信息,请参阅官方 junit 用户指南

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

JUnit 4

If you are still using Junit 4 (I tested with v4.8.2) you can use the Enclosed runner in conjunction with inner classes and the Parameterized runner:

如果您仍在使用 Junit 4(我使用 v4.8.2 进行了测试),您可以将 Enclosed runner 与内部类和参数化 runner 结合使用:

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

回答by AmanicA

I just found outthat one can use JUnitParams. I converted one of my tests now to use it and it works beautifully.

刚刚发现可以使用JUnitParams。我现在转换了我的一个测试来使用它,它运行得很好。

回答by Jeanne Boyarsky

No. The best practice is to move those non-parameterized tests to a different class (.java file)

不。最佳实践是将那些非参数化测试移动到不同的类(.java 文件)

回答by AmanicA

Zohhak test runneris a simpler way to parameterize specific tests. Thanks Piotr!

Zohhak 测试运行器是一种更简单的参数化特定测试的方法。谢谢彼得!

回答by Phil Ninan

I was able to do something very similar to Matthew Madson answer and found it useful to create a Base Class to encapsulate setup and common helper functions between the single and param tests. This works without using Enclosed.class.

我能够做一些与 Matthew Madson 的回答非常相似的事情,并发现创建一个基类来封装单项测试和参数测试之间的设置和通用辅助函数很有用。这在不使用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
        ...
    }
}

回答by Muhammad Azam

I stuck in this problem while I'm writing test in spring boot MockMvc I simply created two classes in separate java files ( one for ParameterizedTestand other for SingleTest) and create a suite for them. because inner classes were creating error for static members and and not static members and class.

我在 spring boot MockMvc 中编写测试时遇到了这个问题,我只是在单独的 java 文件中创建了两个类(一个用于ParameterizedTest,另一个用于SingleTest)并为它们创建一个套件。因为内部类正在为静态成员而不是静态成员和类创建错误。

回答by ch271828n

For those who wants the parameters come from java functioninstead of annotations:

对于那些希望参数来自java 函数而不是注释的人

@ParameterizedTest
@MethodSource("provideStringsForIsBlank")
void isBlank_ShouldReturnTrueForNullOrBlankStrings(String input, boolean expected) {
    assertEquals(expected, Strings.isBlank(input));
}

private static Stream<Arguments> provideStringsForIsBlank() {
    return Stream.of(
      Arguments.of(null, true),
      Arguments.of("", true),
      Arguments.of("  ", true),
      Arguments.of("not blank", false)
    );
}

source: https://www.baeldung.com/parameterized-tests-junit-5

来源:https: //www.baeldung.com/parameterized-tests-junit-5

回答by AmanicA

It appears that TestNG does not suffer from this problem. I'm not that desperate so I modified the builtin Parameterized class to support this feature. Just annotate applicable tests as @NonParameterized. Note that this class only works with its on annotations, i.e. check your imports.

看来,TestNG的不存在这个问题。我并没有那么绝望,所以我修改了内置的 Parameterized 类来支持这个特性。只需将适用的测试注释为@NonParameterized。请注意,此类仅适用于其 on 注释,即检查您的导入。

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

}

Update: I'm trying to get this sort of thing added to junit.

更新:我正在尝试将这种东西添加到 junit 中。

回答by Artem Oboturov

Assuming you use Parametrized.class to run your test class - mark all non-parametrized tests as @Ignored. Otherwise you can make a static inner class to group all your parametrized tests and another - for non-parametrized.

假设您使用 Parametrized.class 来运行您的测试类 - 将所有非参数化测试标记为 @Ignored。否则,您可以创建一个静态内部类来将所有参数化测试和另一个 - 用于非参数化。

回答by Chee Loong Soon

I did something similar to Matthew's Solution. However, I created two new java files that extends the current file so that the ComponentSingleTests do not run twice. This way, they can share common member variables and helper methods from the parent class. The problem I had with Matthew's Solution was that my single test was running twice instead of once as the Enclosed.class (which extends the Suite.class) will make your test run twice as described in this link Prevent junit tests from running twice

我做了类似于马修解决方案的事情。但是,我创建了两个新的 java 文件来扩展当前文件,以便 ComponentSingleTests 不会运行两次。这样,它们就可以共享来自父类的公共成员变量和辅助方法。我在 Matthew's Solution 中遇到的问题是,我的单个测试运行了两次而不是一次,因为 Enclosed.class(它扩展了 Suite.class)将使您的测试运行两次,如本链接中所述 防止 junit 测试运行两次

ComponentTest.java

组件测试.java

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

ComponentParamTests.java

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

ComponentSingleTests.java

import org.junit.Test;

public class ComponentSingleTests {

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