-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce support for zero invocations in test templates and parameterized tests #1477
Comments
Tentatively slated for 5.3 RC1 for team discussion |
@johnchurchill Could you please provide a few more details about your use case, particularly how you've written your tests? I assume it involves base classes that inherit |
I have also been trying to envision what you are trying to do and was not able. My best guess is that you have some sort of It would be great if you could provide a skeleton test that exhibits your issue. |
Yes, there is a superclass of all tests which does the parameter loading and execution. There are thousands of subclasses that implement all kinds of functionality. Each (non-test) class is the logic behind an interactive web page in the system. Here is a boiled-down version of that superclass: @ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringDevTestServerConfiguration.class)
@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class })
@ComponentScan(lazyInit = true)
@TestInstance(PER_CLASS)
public abstract class AbstractConfigTestClass implements ApplicationContextAware {
protected List<SingleConfigTestDescriptor> configTests() {
String testClassName = this.getClass().getName();
// remove "Test" on the end to obtain the config class
String clzName = testClassName.substring(0, testClassName.length() - 4);
try {
Class configClz = Class.forName(clzName);
List<SingleConfigTestDescriptor> params = ConfigToTestParams.getTestParameters(configClz);
if (params.size() == 0) {
params.add(null); // need to add null here to avoid junit 5 failing
}
return params;
}
catch (ClassNotFoundException e1) {
throw new RuntimeException("Could not find expected class: " + clzName, e1);
}
}
@ParameterizedTest
@MethodSource("configTests")
public void runTest(SingleConfigTestDescriptor test) {
if (test == null) {
return; // nothing to do; this is the null to avoid the junit 5 failure
}
// use the test data to run the test against the class
}
} By the time we know that there are no available tests to run, it's too late.. JUnit 5 will throw an exception. There are 6 lines in there whose only purpose is to make it run successfully like it did under JUnit 4. I just started working with JUnit 5 a couple of days ago, so I haven't had the chance to look into using |
In any case, we should improve the error message.
The difference being "argument" vs. "invocation context". And the invocation context can still supply zero "arguments" to the method, though that's not the topic of this issue. |
FYI: I reworded the title of this issue accordingly. |
Keep in mind that a |
In other words, see |
I took a look at public class FooTest {
@ParameterizedTest
@MethodSource("data")
void bar1(String s) {
}
@Test
public void bar2() {
// pass
}
public static List<String> data() {
// pretend we have no test cases in the QA database
return new ArrayList<>();
}
} |
Well, that's only partially true. It's a breaking change in the sense that configuration errors previously caused the parameterized test container to fail; whereas, now such configuration errors would be silently ignored. Of course, your use case is not a "configuration error". So we have to decide how to best support both scenarios. 😉 I suppose, if we wanted to support zero invocations, we could simply generate a log message informing the user. The tricky part is what log level to choose. Is that |
In such cases, I don't think the parameterized test container should be marked as "successful" (or somehow ignored, whatever was the case when you implemented that PoC). Rather, I think we should mark the parameterized test container as "skipped" and provide a "reason" why it was skipped. @junit-team/junit-lambda, thoughts? |
As a side note, since I'm the author of the Spring TestContext Framework... @johnchurchill, out of curiosity, why do you disable all Spring test listeners except That's enabled by default. Were other listeners somehow causing problems? |
This case reminds me of the similar case #1298 which was handled by the The similarity is that they both exhibit the same core dilemma: The difference between the two is the default is reversed.
Maybe we could take a similar approach also in the case- with some sort of flag or parameter eg on the annotation. For me personally I think the current default to fail if no elements are present and in esense guarding against error is important. I seen many case where something was silently turned off due to error and the error was missed. |
I don't think invocation context would be more meaningful for the normal user. Though indeed it is more meaningful for someone who understands the internal of the platform. If you consult the relevant section in the User Guide about |
Sure.... "invocation context" is very technical. How about the following instead?
|
Good points, @gaganis.
I tend to agree that adding an opt-in flag as a annotation attribute in How about the following? @ParameterizedTest(failIfNoArgumentsProvided = false) ... with the Though, |
Maybe the following is better: @ParameterizedTest(requireArguments = false) |
That is exactly was I was thinking now too :) |
@johnchurchill, would the following proposal suit your needs? @ParameterizedTest(requireArguments = false) |
FYI: I improved the error message in b1e533c. |
Yes, requireArguments = false would suit my needs perfectly. I hate to see more corner-case parameters being introduced though. The simple API is what makes JUnit so approachable. |
Thanks for the feedback.
Well, therein lies the rub: We can't have our cake and eat it, too. As already pointed out, switching the behavior would no longer be user friendly regarding user configuration errors. So, IMHO, the only viable option is to make it an opt-in feature while retaining backwards compatibility and upholding UX. Of course, if anyone has any better ideas, I'm all ears. |
Thanks for all of the comments, everyone! We will discuss this topic again within the team. |
Regarding the comment
I can hardly imagine such a configuration error, can you name one? |
Yes. @ParameterizedTest
@MethodSource
void strings(String str) {
}
static List<String> strings() {
return List.of();
} That's an obvious user/configuration error. |
Team Decision
|
ParameterizedTestExtension
does not allow zero invocations
We would also benefit from this fix. Is any help required here? |
@nskvortsov A PR implementing the above team decision would be welcome! |
Add a flag to ParameterizedTest to control arguments requirement. This allows users to explicitly opt out from validation of arguments set count and silently skip a test if no arguments are provided In general, support TestTemplateInvocationContextProvider returning zero invocation contexts. Such providers must override new interface method to indicate that the framework should expect "no context returned" Resolves junit-team#1477
Add a flag to ParameterizedTest to control arguments requirement. This allows users to explicitly opt out from validation of arguments set count and silently skip a test if no arguments are provided In general, support TestTemplateInvocationContextProvider returning zero invocation contexts. Such providers must override new interface method to indicate that the framework should expect "no context returned" Resolves junit-team#1477
Here it is 🙂, |
Can I help in any way to proceed with code review of the pull request? |
No, I don't think so. It's just a matter of someone from the core team taking a proper look. |
Dear team, I would be happy to improve my pull request. Please give me some feedback on it ) |
@nskvortsov Sorry for the delay! I have reviewed the PR just now. |
Add a flag to ParameterizedTest to control arguments requirement. This allows users to explicitly opt out from validation of arguments set count and silently skip a test if no arguments are provided In general, support TestTemplateInvocationContextProvider returning zero invocation contexts. Such providers must override new interface method to indicate that the framework should expect "no context returned" Resolves junit-team#1477
Overview
Using: JUnit: 5.2.0
Great job on the new
ParameterizedTest
support in v.5. The replacement of the static, one-per-class Parameters annotation with more flexibleMethodSource
, etc. has been like a breath of fresh air and allowed me to remove thousands (!!) of lines of supporting code from my system. I'm really loving it.However, someone decided to disallow zero parameters with this precondition check in
ParameterizedTestExtension
.The problem with this is that we have some testing situations where parameterized tests with zero parameters are not exceptional. For example, we run tests against thousands of a certain type of class generically against a database of past production failures, and many of these classes have never experienced a production failure. When the tests are run, we now get failures due the above precondition check. JUnit 4 handled this cleanly: it would simply not run any tests on those classes.
Workaround
I can get around this by adding a
null
to the method creating the collection of parameters if it is empty, and then return from the beginning of the@ParameterizedTest
method code if the passed parameter isnull
. That lets us continue to run the parameterized tests against all of the classes, but comes with some disadvantages:null
causes the test count inflation for these "phantom" tests.null
is now reserved as a signal for no parameters, rather than something wrong in the parameter creation.Proposal
If nobody has any strong feelings about disallowing the no-parameter case, can we just have this precondition removed from a future version?
Thanks.
The text was updated successfully, but these errors were encountered: