Skip to content
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

DisableIfArgument #368

Merged
merged 36 commits into from
May 29, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d99e74b
Created PoC extension for disabling parameterized tests
Michael1993 Sep 26, 2020
62bf604
Merge branch 'master' into issue313/explore-invocation-interceptor
Michael1993 Oct 31, 2020
d223cfd
Replaced value() with contains() and matches()
Michael1993 Oct 31, 2020
9574ea8
Disable based on containing and RegEx
Michael1993 Oct 31, 2020
0168cc1
Updated tests
Michael1993 Oct 31, 2020
c4e3576
Spotless, no :(
Michael1993 Oct 31, 2020
f9f46c8
Add documentation based on DisableIfDisplayName
Michael1993 Nov 2, 2020
81fddd9
README.md updated to include contribution
Michael1993 Nov 2, 2020
98663ed
Simplify orElseThrow
Michael1993 Nov 2, 2020
2cfc8c1
Update tests to include a poem (Oscar Wilde: Requiescat)
Michael1993 Nov 2, 2020
b85e3e0
Update package-info.java with the new addition
Michael1993 Nov 2, 2020
5643ebf
Fix comma typo
Michael1993 Nov 2, 2020
10e4d8a
Add references to contribution
Michael1993 Nov 2, 2020
bbe9dd0
Update code and documentation according to feedback
Michael1993 Nov 10, 2020
26fdcb5
In progress DisableIfParameter
Michael1993 Nov 24, 2020
ff66f85
No parameters can not be disabled.
Michael1993 Dec 22, 2020
4b8d27b
Update DisableIfParameter based on feedback
Michael1993 Mar 23, 2021
c3459d9
Merge remote-tracking branch 'origin/main' into issue313/explore-invo…
Michael1993 Mar 23, 2021
a5bd7ca
Update tests and formatting
Michael1993 Mar 23, 2021
b0e08e3
Update tests
Michael1993 Mar 23, 2021
8bb3765
Invalid input returns exception
Michael1993 Mar 23, 2021
6903f5b
Invalid input returns exception, try #2
Michael1993 Mar 23, 2021
b6886d0
Add another test
Michael1993 Mar 23, 2021
87637cb
Update docs
Michael1993 Apr 6, 2021
6c32440
Merge branch 'main' into issue313/explore-invocation-interceptor
Michael1993 Apr 8, 2021
cf6d5dc
Trigger checks again
Michael1993 Apr 8, 2021
faf3d60
Add JavaDoc on annotations
Michael1993 Apr 17, 2021
6d99b67
More JavaDoc updates
Michael1993 Apr 17, 2021
612030f
Minor documentation polish
Michael1993 Apr 18, 2021
b905274
Merge branch 'main' into issue313/explore-invocation-interceptor
Michael1993 Apr 21, 2021
960051f
Merge branch 'main' into issue313/explore-invocation-interceptor
May 18, 2021
76aa6d8
Edit documentation
May 18, 2021
4fad9b5
Moving code around
May 18, 2021
058a1a2
Rename "Parameter" to "Argument"
May 18, 2021
7f8c331
Merge branch 'main' of https://github.com/junit-pioneer/junit-pioneer…
Michael1993 May 29, 2021
27bd4fb
Add since in javadoc
Michael1993 May 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ The least we can do is to thank them and list some of their accomplishments here
* [Ignat Simonenko](https://github.com/simonenkoi) fixed a noteworthy bug in the default locale extension (#146 / #161)
* [Mark Rösler](https://github.com/Hancho2009) contributed the [environment variable extension](https://junit-pioneer.org/docs/environment-variables/) (#167 / #174 and #241 / #242)
* [Matthias Bünger](https://github.com/Bukama) opened, vetted, and groomed countless issues and PRs and contributed multiple refactorings (e.g. #165 / #168) and fixes (e.g. #190 / #200) before getting promoted to maintainer
* [Mihály Verhás](https://github.com/Michael1993) contributed [the StdIO extension](https://junit-pioneer.org/docs/standard-input-output/) (#34 / #227), [the ReportEntryExtension](https://junit-pioneer.org/docs/report-entries/) (#134, #179 / #183, #216, #294), added tests to other extensions (#164 / #272), the Pioneer assertions and contributed to multiple issues (e.g. #217 / #298) and PRs (e.g. #253, #307)
* [Mihály Verhás](https://github.com/Michael1993) contributed [the StdIO extension](https://junit-pioneer.org/docs/standard-input-output/) (#34 / #227), [the ReportEntryExtension](https://junit-pioneer.org/docs/report-entries/) (#134, #179 / #183, #216, #294), [the DisableIfParameterExtension](https://junit-pioneer.org/docs/disable-if-parameter/) (#313, #368), added tests to other extensions (#164 / #272), the Pioneer assertions and contributed to multiple issues (e.g. #217 / #298) and PRs (e.g. #253, #307)
* [Nishant Vashisth](https://github.com/nishantvas) contributed an [extension to disable parameterized tests](https://junit-pioneer.org/docs/disable-if-display-name/) by display name (#163 / #175)
* [Simon Schrottner](https://github.com/aepfli) contributed to multiple issues and PRs and almost single-handedly revamped the build and QA process (e.g. #192 / #185) before getting promoted to maintainer
* [Sullis](https://github.com/sullis) improved GitHub Actions with Gradle Wrapper Validation check (#302)
Expand Down
83 changes: 83 additions & 0 deletions docs/disable-if-parameter.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
:page-title: Disable Parameterized Test Based on Parameter Values
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
:page-description: Extends JUnit Jupiter with `@DisableIfParameter`, which selectively disables parameterized tests

The `@DisableIfParameter` annotation can be used to selectively disable parameterized tests based on their parameter values (converted with `toString()`).
The annotation is only supported on test method level for parameterized tests.
Unlike the `@Disabled` API provided in JUnit Jupiter, which disables the test on first encounter of the annotation , `@DisableIfParameter` is validated before each parameterized test execution.
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
As a consequence, instead of disabling the entire set of parameterized tests, each test can be evaluated and possibly disabled individually.
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved

[source,java]
----
// disable invocations whose parameter contains "she"
@DisableIfParameter(contains = "she")
@ParameterizedTest
@ValueSource(strings = {
"Tread lightly, she is near",
"Under the snow,",
"Speak gently, she can hear",
"The daisies grow."
})
void interceptContains(String line) {
}
----

The test `interceptContains` ordinarily would run four times, but because the first and third lines contain the word "she", those invocations get disabled.

You can specify more than one substring at a time:

[source,java]
----
@DisableIfParameter(contains = { "bright", "dust" })
@ParameterizedTest
@CsvSource(delimiter = ';', value = {
"All her bright golden hair;Tarnished with rust,",
"She that was young and fair;Fallen to dust."
})
void interceptContainsAny(String line, String line2) {
}
----

`DisableIfParameter` will disable the test invocation if any parameter value matches any value from `contains`.
In this case, the test `interceptContainsAny` should have run two times, but both invocations got disabled.
The first invocation because the first parameter contains "bright".
The second invocation because the second parameter contains "dust".
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved

If substrings are not powerful enough, you can also use regular expressions:
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved

[source,java]
----
// disable invocations whose parameter ends with 'knew' or 'grew'
@DisableIfParameter(matches = { ".*knew", ".*grew" })
@ParameterizedTest
@ValueSource(strings = {
"Lily-like, white as snow,",
"She hardly knew",
"She was a woman, so",
"Sweetly she grew"
})
void interceptMatches(String value) {
}
----

The second invocation has a parameter that ends with "knew", so it gets disabled.
The fourth invocation has a parameter that ends with "grew", so it gets disabled.

Just like with `contains`, if any parameter value matches any expression from `matches`, the invocation gets disabled.

You can use both, in which case a test is disabled if any parameter value contains a substring _or_ matches an expression:
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved

[source,java]
----
@ParameterizedTest
@DisableIfParameter(
contains = { "sonnet", "life" },
matches = "^.*(.+)\\1.*$")
@ValueSource(strings = {
"Peace, Peace, she cannot hear",
"Lyre or sonnet,",
"All my life’s buried here,",
"Heap earth upon it."
})
void interceptBoth(String value) {
}
----
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions docs/docs-nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
url: /docs/default-locale-timezone/
- title: "Disable Based on DisplayName"
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
url: /docs/disable-if-display-name/
- title: "Disable Parameterized Test Based on Parameter Values"
url: /docs/disable-if-parameter/
- title: "Publishing Report Entries"
url: /docs/report-entries/
- title: "Range Sources"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package org.junitpioneer.jupiter.params;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.extension.ExtendWith;

/**
* {@code @DisableIfParameter} is a JUnit Jupiter extension that can
* be used to selectively disable a {@link org.junit.jupiter.params.ParameterizedTest}
* based on their parameter values as defined by {@link Object#toString()}.
*
* <p>The extension utilizes Jupiter's {@link org.junit.jupiter.api.extension.InvocationInterceptor}.
* It's important to note that since it's marked as {@link org.apiguardian.api.API.Status#EXPERIMENTAL}
* it might be removed without prior notice.
* Unlike {@link org.junit.jupiter.api.Disabled} annotations, this extension doesn't disable the whole test method.
* With {@code DisableIfParameter}, it is possible to selectively disable tests out of the plethora
* of dynamically registered parameterized tests.</p>
*
* <p>If neither {@link DisableIfParameter#contains() contains} nor
* {@link DisableIfParameter#matches() matches} is configured, the extension will throw an exception.
* It is possible to configure both, in which case the test gets disabled if at least one substring
* was found <em>or</em> at least one regular expression matched.</p>
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
*
* @see DisableIfParameterExtension
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisableIfParameterExtension.class)
public @interface DisableIfParameter {

String[] contains() default {};

String[] matches() default {};
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package org.junitpioneer.jupiter.params;

import static java.lang.String.format;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.jupiter.api.extension.ExtensionConfigurationException;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import org.junit.platform.commons.support.AnnotationSupport;
import org.opentest4j.TestAbortedException;

class DisableIfParameterExtension implements InvocationInterceptor {

@Override
public void interceptTestTemplateMethod(Invocation<Void> invocation,
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
Method testMethod = extensionContext.getRequiredTestMethod();
DisableIfParameter annotation = AnnotationSupport
.findAnnotation(testMethod, DisableIfParameter.class)
.orElseThrow(() -> new ExtensionConfigurationException("@DisableIfParameter is missing"));
if (annotation.contains().length == 0 && annotation.matches().length == 0)
throw new ExtensionConfigurationException(
format("%s requires that either `contains` or `matches` is specified, but both are empty.",
DisableIfParameter.class.getSimpleName()));
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
// Check if any argument contains any element from 'contains'
Michael1993 marked this conversation as resolved.
Show resolved Hide resolved
if (invocationContext
.getArguments()
.stream()
.anyMatch(arg -> Arrays.stream(annotation.contains()).anyMatch(arg.toString()::contains)))
throw new TestAbortedException("One or more arguments contained a value from the `contains` array.");
// Check if any argument matches any element from 'matches'
if (invocationContext
.getArguments()
.stream()
.anyMatch(arg -> Arrays.stream(annotation.matches()).anyMatch(arg.toString()::matches)))
throw new TestAbortedException(
"One or more arguments matched a regular expression from the `matches` array.");
invocation.proceed();
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/**
* Argument providers for a range of numbers.
*
* Disable ParameterizedTest based on conditions.
* <p>Check out the following types for details:
* <ul>
* <li>{@link org.junitpioneer.jupiter.params.DisableIfDisplayName}</li>
* <li>{@link org.junitpioneer.jupiter.params.DisableIfParameter}</li>
* </ul>
*
* Argument providers for a range of numbers.
* <p>Check out the following types for details on providing values for parameterized tests:
* <ul>
* <li>{@link org.junitpioneer.jupiter.params.ByteRange}</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package org.junitpioneer.jupiter.params;

import static org.junitpioneer.testkit.assertion.PioneerAssert.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junitpioneer.testkit.ExecutionResults;
import org.junitpioneer.testkit.PioneerTestKit;

/**
* Oscar Wilde: Requiescat is in the public domain
*/
@DisplayName("DisableIfParameterExtension")
class DisableIfParameterExtensionTests {

@Nested
@DisplayName("when configured correctly")
class CorrectConfigurationTests {

@Test
@DisplayName("disables tests when parameter contains any value from the 'contains' array")
void interceptContains() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(CorrectConfigTestCases.class, "interceptContains",
String.class);

assertThat(results).hasNumberOfSucceededTests(2).hasNumberOfAbortedTests(2);
}

@Test
@DisplayName("disables tests when any parameter contains any value from the 'contains' array")
void interceptContainsAny() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(CorrectConfigTestCases.class, "interceptContainsAny",
String.class, String.class);

assertThat(results).hasNumberOfAbortedTests(2);
}

@Test
@DisplayName("disables tests when parameter matches any regex from the 'matches' array")
void interceptMatches() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(CorrectConfigTestCases.class, "interceptMatches",
String.class);

assertThat(results).hasNumberOfSucceededTests(2).hasNumberOfAbortedTests(2);
}

@Test
@DisplayName("disables tests when any parameter matches any regex from the 'matches' array")
void interceptMatchesAny() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(CorrectConfigTestCases.class, "interceptMatchesAny",
String.class, String.class);

assertThat(results).hasNumberOfAbortedTests(2);
}

@Test
@DisplayName("disables tests if parameter matches regex from 'matches' or contains value from 'contains'")
void interceptMatchesAndContains() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(CorrectConfigTestCases.class, "interceptBoth", String.class);

assertThat(results).hasNumberOfSucceededTests(1).hasNumberOfAbortedTests(3);
}

}

@Nested
@DisplayName("when not configured correctly")
class MisconfigurationTests {

@Test
@DisplayName("throws an exception if both 'matches' and 'contains' is missing")
void missingValues() {
ExecutionResults results = PioneerTestKit
.executeTestMethodWithParameterTypes(BadConfigTestCases.class, "missingValues", String.class);

assertThat(results).hasNumberOfFailedTests(3);
}

}
Bukama marked this conversation as resolved.
Show resolved Hide resolved

static class CorrectConfigTestCases {

@ParameterizedTest
@DisableIfParameter(contains = "she")
@ValueSource(strings = { "Tread lightly, she is near", "Under the snow,", "Speak gently, she can hear",
"The daisies grow." })
void interceptContains(String line) {
}

@ParameterizedTest
@DisableIfParameter(contains = { "bright", "dust" })
@CsvSource(delimiter = ';', value = { "All her bright golden hair;Tarnished with rust,",
"She that was young and fair;Fallen to dust." })
void interceptContainsAny(String line, String line2) {
}

@ParameterizedTest
@DisableIfParameter(matches = { ".*knew", ".*grew" })
@ValueSource(strings = { "Lily-like, white as snow,", "She hardly knew", "She was a woman, so",
"Sweetly she grew" })
void interceptMatches(String value) {
}

@ParameterizedTest
@DisableIfParameter(matches = ".*hea?rt?.*")
@CsvSource(delimiter = ';', value = { "Coffin-board, heavy stone,;Lie on her breast,",
"I vex my heart alone;She is at rest." })
void interceptMatchesAny(String line, String line2) {
}

@ParameterizedTest
@DisableIfParameter(contains = { "sonnet", "life" }, matches = "^.*(.+)\\1.*$")
@ValueSource(strings = { "Peace, Peace, she cannot hear", "Lyre or sonnet,", "All my life’s buried here,",
"Heap earth upon it." })
void interceptBoth(String value) {
}

}

static class BadConfigTestCases {

@ParameterizedTest
@DisableIfParameter
@ValueSource(strings = { "A", "B", "C" })
void missingValues(String value) {
}

}

}