-
-
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
Support range filter in @EnumSource
#4185
Comments
EnumSource
@EnumSource
For this you can use EnumSet and LazyParams to cook your own parameterization solution ... import java.util.EnumSet;
import static org.lazyparams.LazyParams.pickValue;
public class EnumParams {
public static <E extends Enum<E>> E pickFromRange(
String paramName, E from, E to) {
if (null == from) {
from = EnumSet.allOf(to.getDeclaringClass()).stream()
.findFirst().get();
} else if (null == to) {
to = EnumSet.allOf(from.getDeclaringClass()).stream()
.sorted(Comparator.reverseOrder())
.findFirst().get();
}
return (E) pickValue( paramName, EnumSet.range(from, to).toArray());
}
} The above method can then be used to achieve parameterization in midair during the execution of a regular JUnit test: @Test
void plus_unsupportedUnits() {
Instant now = Instant.now();
ChronoUnit unit = EnumParams.pickFromRange("unit", ChronoUnit.WEEKS, null);
assertThrows(UnsupportedTemporalTypeException.class, () -> now.plus(1, unit));
}
// With ConsoleLauncher the test result is presented like this:
// JUnit Jupiter ✔
// └─ InstantTests ✔
// └─ plus_unsupportedUnits() ✔
// ├─ plus_unsupportedUnits unit=Weeks ✔
// ├─ plus_unsupportedUnits unit=Months ✔
// ├─ plus_unsupportedUnits unit=Years ✔
// ├─ plus_unsupportedUnits unit=Decades ✔
// ├─ plus_unsupportedUnits unit=Centuries ✔
// ├─ plus_unsupportedUnits unit=Millennia ✔
// ├─ plus_unsupportedUnits unit=Eras ✔
// └─ plus_unsupportedUnits unit=Forever ✔ |
@kaipe LazyParams looks interesting! Is there some documentation explaining how it works? I wonder if this would be a good addition to Pioneer's range sources. @bjmi WDYT? FWIW, this can be achieved using a @ParameterizedTest
@MethodSource
void plus_unsupportedUnits(ChronoUnit unit) {
Instant now = Instant.now();
assertThrows(UnsupportedTemporalTypeException.class, () -> now.plus(1, unit));
}
static Set<ChronoUnit> plus_unsupportedUnits() {
return EnumSet.range(ChronoUnit.WEEKS, ChronoUnit.FOREVER);
} |
Indeed... a bit of magic. 🪄
It's performing bytecode manipulation with a ByteBuddy agent.
I cannot speak for the Pioneer team, but my guess is that bytecode manipulation would be out of scope for a project like Pioneer. |
Maybe not with bytecode manipulation but an |
Yeah, I took a glance and saw that as well. But I was wondering what it manipulated it into. Does it use
That's what I meant as well. |
@kaipe Thank you for sharing the interesting solution using LazyParams approach. @marcphilipp Another approach could just extend
|
Team decision: Introduce new |
@bjmi Do you have time to work on a PR? |
Following up on @marcphilipp's suggestion for an alternative solution in the interim, with recent versions of JUnit Jupiter this can be simplified even further with a @ParameterizedTest
@FieldSource
void plus_unsupportedUnits(ChronoUnit unit) {
Instant now = Instant.now();
assertThrows(UnsupportedTemporalTypeException.class, () -> now.plus(1, unit));
}
static Set<ChronoUnit> plus_unsupportedUnits = EnumSet.range(WEEKS, FOREVER); |
Available documentation only concerns all-pairs testing and lazy parameterization features. I work on some slides to highlight how declarative parameterization features can be implemented with Jupiter extension model. An example is LazyParameterResolver, which is a Jupiter @ExtendWith(LazyParameterResolver.class)
@Retention(RetentionPolicy.RUNTIME)
@interface IntParam { int[] value(); }
@ExtendWith(LazyParameterResolver.class)
@Retention(RetentionPolicy.RUNTIME)
@interface StringParam { String[] value(); } ... which are defined within a demo test-class GlobalAndLocalLifecycleMethodParameters. The punch-line is how a Jupiter public class GlobalAndLocal {
@BeforeAll
static void setupGlobal(@StringParam({"AA","BB"}) String gbl) {/*...*/}
@BeforeEach
void setupLocal(@IntParam({1,2,3}) int local) {/*...*/}
@Test void vanilla() {/*...*/}
@Test void extraParameters(
@StringParam({"foo","bar","buz"}) String extraStr,
@IntParam ({ 41, 42, 43}) int extraInt) {/*...*/}
//With ConsoleLauncher the test result is presented like this:
// JUnit Jupiter ✔
// └─ GlobalAndLocal ✔
// ├─ GlobalAndLocal gbl=AA ✔
// │ ├─ extraParameters(String, int) gbl=AA ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=1 extraStr=foo extraInt=41 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=2 extraStr=bar extraInt=42 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=3 extraStr=buz extraInt=43 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=1 extraStr=bar extraInt=43 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=3 extraStr=foo extraInt=42 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=2 extraStr=buz extraInt=41 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=1 extraStr=buz extraInt=42 ✔
// │ │ ├─ extraParameters(String, int) gbl=AA / local=2 extraStr=foo extraInt=43 ✔
// │ │ └─ extraParameters(String, int) gbl=AA / local=3 extraStr=bar extraInt=41 ✔
// │ └─ vanilla gbl=AA ✔
// │ ├─ vanilla gbl=AA / local=1 ✔
// │ ├─ vanilla gbl=AA / local=2 ✔
// │ └─ vanilla gbl=AA / local=3 ✔
// └─ GlobalAndLocal gbl=BB ✔
// ├─ extraParameters(String, int) gbl=BB ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=1 extraStr=foo extraInt=41 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=2 extraStr=bar extraInt=42 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=3 extraStr=buz extraInt=43 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=1 extraStr=bar extraInt=43 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=3 extraStr=foo extraInt=42 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=2 extraStr=buz extraInt=41 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=1 extraStr=buz extraInt=42 ✔
// │ ├─ extraParameters(String, int) gbl=BB / local=2 extraStr=foo extraInt=43 ✔
// │ └─ extraParameters(String, int) gbl=BB / local=3 extraStr=bar extraInt=41 ✔
// └─ vanilla gbl=BB ✔
// ├─ vanilla gbl=BB / local=1 ✔
// ├─ vanilla gbl=BB / local=2 ✔
// └─ vanilla gbl=BB / local=3 ✔
} (By implementing a Jupiter But the above is all about how-to-use LazyParams and I suspect this crowd would enjoy documentation on LazyParams' technical solution ...
... which is not based on If you think the comment field of a feature request is a suitable forum(?), then I could go into detail on how the additional |
@kaipe Thanks for that!
No, I don't think this the right place. Maybe a wiki page on your repo? But you don't need to write that just for me. |
Hello, I want to contribute to this, but I have a question regarding this use case: @ParameterizedTest
@EnumSource(mode = EXCLUDE, names = { "HOURS", "DAYS" }, from = "HOURS", to = "YEARS")
void testWithEnumSourceExcludeRange(ChronoUnit unit) {
// test
} I noticed that |
You mean because |
I see. I initially thought I needed to merge the sets of |
Sometimes it is necessary to test a consecutive range of enum constants in a unit test. The existing matchers of
@EnumSource
can't help here.As Enum constants have a natural order, it would be useful to support a range in
@EnumSource
. If a bound is not specified, it is considered unbounded.Following example would work then: all constants of
ChronoUnit
starting atWEEKS
aren't supported byInstant.plus(..)
.Candidates are
@EnumSource(from = "..", to = "..")
,@EnumSource(begin = "..", end = "..")
or@EnumSource(start = "..", end = "..")
The existing matchers could be applied to a specified range additionally.
Deliverables
@EnumSource
with optionalfrom
andto
attributesThe text was updated successfully, but these errors were encountered: