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
Excluding a non param test in parameterized test class
提问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
回答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>
* @RunWith(Parameterized.class)
* public class FibonacciTest {
* @Parameters
* public static List<Object[]> 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;
* }
*
* @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>@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.
回答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 {
...
}
}

