Is there any annotation in JUnit to exclude a non param test in parameterized test class?
-
What is the intention? where do you expect @Parameters use in your test case? – 卢声远 Shengyuan Lu Jan 27 '11 at 05:18
9 Answers
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 guide for details:
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
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:
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 {
}
}
}

- 3,926
- 2
- 28
- 49

- 1,643
- 13
- 24
-
3Nice and clear solution, you get a good separation between parameterized and single tests within the same module. – Robert Jul 08 '15 at 07:34
-
3Nice solution, however, it runs the test twice! The Enclosed.class is an extension of the Suite.class which causes all Suite to run twice instead of the number of parameters in this case. – Chee Loong Soon Mar 29 '16 at 18:08
-
1I did the same. But I want the `nonParametrized` test to run only after `Parametrized` ones. Couldn't make it run that way. I tried `FixMethodOrder` too. – balki Oct 07 '16 at 01:16
-
1
No. The best practice is to move those non-parameterized tests to a different class (.java file)

- 12,156
- 2
- 49
- 59
-
28One module = one test class is another best practice that this practice conflicts with. It's more like a workaround than a best practice. – Jilles van Gurp Jul 30 '12 at 15:29
-
Do you have any justification for this assertion? This would mean that a given class would have tests in two different test classes, which also strikes me as a bad idea. – scubbo Jun 04 '18 at 21:24
-
1@scubbo I don't see why it is a bad idea to have tests in two different classes. I've done that for some classes even without parameterized tests. For example Foo_SecurityTest, Foo_BusinessLogicTest. – Jeanne Boyarsky Jun 19 '18 at 01:04
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.
@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
...
}
}

- 1,108
- 1
- 14
- 23
-
Good solution. Only thing I'd add is that you can annotate the outer class, ComponentTest, with `@RunWith(Suite.class)` and `@SuiteClasses({ ComponentTest.ComponentParamTests.class, ComponentTest.ComponentParamTests.class })` to make it runnable. It would then run all tests in both the nested classes. Source: http://junit.sourceforge.net/javadoc/org/junit/runners/Suite.html – JCoster22 Nov 01 '17 at 18:01
-
That worked nicely for when I want to run all tests in this class, previously I had to select one or the other. However, both classes did run when running unit tests at a package / project level. Thanks! – Phil Ninan Nov 14 '17 at 16:57
Zohhak test runner is a simpler way to parameterize specific tests. Thanks Piotr!

- 4,659
- 1
- 34
- 49
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
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 {
...
}
}

- 1
- 1

- 3,601
- 3
- 17
- 21
-
This was quite helpful in my case where adding Enclosed.class was running the tests twice. – Akshat Feb 06 '23 at 18:12
For those who wants the parameters come from java function instead of annotations:
@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

- 15,854
- 5
- 53
- 88
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.
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.

- 4,659
- 1
- 34
- 49
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.

- 4,344
- 2
- 30
- 48
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 ParameterizedTest and 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.

- 535
- 7
- 12