Skip to content

Commit 67d3508

Browse files
authored
Merge pull request #309 from sagaofsilence/sr-junit5-functional-interfaces
Sr junit5 functional interfaces
2 parents 9c6ceb5 + 12f71f6 commit 67d3508

File tree

7 files changed

+361
-0
lines changed

7 files changed

+361
-0
lines changed

build-all.sh

+2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ then
211211
build_gradle_module "spring-data/spring-data-jdbc-converter"
212212
build_gradle_module "reactive"
213213
build_gradle_module "junit/assumptions"
214+
build_maven_module "junit/junit5/junit5"
215+
build_maven_module "junit/junit5/functional-interfaces"
214216
build_gradle_module "logging"
215217
build_gradle_module "pact/pact-feign-consumer"
216218

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
6+
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
15+
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
21+
22+
.vscode
23+
24+
### NetBeans ###
25+
/nbproject/private/
26+
/nbbuild/
27+
/dist/
28+
/nbdist/
29+
/.nb-gradle/
30+
build/
31+
!**/src/main/**/build/
32+
!**/src/test/**/build/
33+
34+
### VS Code ###
35+
.vscode/
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>io.reflectoring</groupId>
8+
<artifactId>junit5-functional-interfaces</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.source>17</maven.compiler.source>
13+
<maven.compiler.target>17</maven.compiler.target>
14+
<junit.version>5.9.0</junit.version>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.junit.jupiter</groupId>
20+
<artifactId>junit-jupiter-api</artifactId>
21+
<version>${junit.version}</version>
22+
<scope>test</scope>
23+
</dependency>
24+
25+
<dependency>
26+
<groupId>org.junit.jupiter</groupId>
27+
<artifactId>junit-jupiter-params</artifactId>
28+
<version>${junit.version}</version>
29+
<scope>test</scope>
30+
</dependency>
31+
32+
<dependency>
33+
<groupId>org.hamcrest</groupId>
34+
<artifactId>hamcrest-all</artifactId>
35+
<version>1.3</version>
36+
<scope>test</scope>
37+
</dependency>
38+
39+
</dependencies>
40+
41+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.reflectoring.functional;
2+
3+
public class ValidationException extends Throwable {
4+
public ValidationException(String message) {
5+
super(message);
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package io.reflectoring.functional;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.time.Duration;
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import java.util.concurrent.TimeUnit;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.function.Executable;
12+
import org.junit.jupiter.params.ParameterizedTest;
13+
import org.junit.jupiter.params.provider.CsvSource;
14+
import org.opentest4j.AssertionFailedError;
15+
16+
public class ExecutableTest {
17+
private final List<Long> numbers = Arrays.asList(100L, 200L, 50L, 300L);
18+
final Executable sorter =
19+
() -> {
20+
TimeUnit.SECONDS.sleep(2);
21+
numbers.sort(Long::compareTo);
22+
};
23+
private final Executable checkSorting =
24+
() -> assertEquals(List.of(50L, 100L, 200L, 300L), numbers);
25+
private final Executable noChanges = () -> assertEquals(List.of(100L, 200L, 50L, 300L), numbers);
26+
27+
@ParameterizedTest
28+
@CsvSource({"1,1,2,Hello,H,bye,2,byebye", "4,5,9,Good,Go,Go,-10,", "10,21,31,Team,Tea,Stop,-2,"})
29+
void testAssertAllWithExecutable(
30+
int num1,
31+
int num2,
32+
int sum,
33+
String input,
34+
String prefix,
35+
String arg,
36+
int count,
37+
String result) {
38+
assertAll(
39+
() -> assertEquals(sum, num1 + num2),
40+
() -> assertTrue(input.startsWith(prefix)),
41+
() -> {
42+
if (count < 0) {
43+
assertThrows(
44+
IllegalArgumentException.class,
45+
() -> {
46+
new ArrayList<>(count);
47+
});
48+
} else {
49+
assertEquals(result, arg.repeat(count));
50+
}
51+
});
52+
}
53+
54+
@ParameterizedTest
55+
@CsvSource({"one,0,o", "one,1,n"})
56+
void testAssertDoesNotThrowWithExecutable(String input, int index, char result) {
57+
assertDoesNotThrow(() -> assertEquals(input.charAt(index), result));
58+
}
59+
60+
@Test
61+
void testAssertThrowsWithExecutable() {
62+
List<String> input = Arrays.asList("one", "", "three", null, "five");
63+
final IllegalArgumentException exception =
64+
assertThrows(
65+
IllegalArgumentException.class,
66+
() -> {
67+
for (String value : input) {
68+
if (value == null || value.isBlank()) {
69+
throw new IllegalArgumentException("Got invalid value");
70+
}
71+
// process values
72+
}
73+
});
74+
assertEquals("Got invalid value", exception.getMessage());
75+
}
76+
77+
@Test
78+
void testAssertTimeoutWithExecutable() {
79+
assertAll(
80+
() ->
81+
assertThrows(
82+
AssertionFailedError.class, () -> assertTimeout(Duration.ofSeconds(1), sorter)),
83+
checkSorting);
84+
85+
assertAll(
86+
() -> assertDoesNotThrow(() -> assertTimeout(Duration.ofSeconds(5), sorter)), checkSorting);
87+
}
88+
89+
@Test
90+
void testAssertTimeoutPreemptivelyWithExecutable() {
91+
assertAll(
92+
() ->
93+
assertThrows(
94+
AssertionFailedError.class,
95+
() -> assertTimeoutPreemptively(Duration.ofSeconds(1), sorter)),
96+
noChanges);
97+
98+
assertAll(
99+
() -> assertDoesNotThrow(() -> assertTimeoutPreemptively(Duration.ofSeconds(5), sorter)),
100+
checkSorting);
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package io.reflectoring.functional;
2+
3+
import static org.junit.jupiter.api.Assertions.assertAll;
4+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
8+
import java.text.MessageFormat;
9+
import java.time.temporal.ValueRange;
10+
import java.util.Arrays;
11+
import java.util.Collection;
12+
import java.util.function.Function;
13+
import java.util.stream.Stream;
14+
import org.junit.jupiter.api.DynamicTest;
15+
import org.junit.jupiter.api.TestFactory;
16+
import org.junit.jupiter.api.function.ThrowingConsumer;
17+
import org.junit.jupiter.params.ParameterizedTest;
18+
import org.junit.jupiter.params.provider.CsvSource;
19+
20+
public class ThrowingConsumerTest {
21+
@ParameterizedTest
22+
@CsvSource({"50,true", "130,false", "-30,false"})
23+
void testMethodThatThrowsCheckedException(int percent, boolean valid) {
24+
// acceptable percentage range: 0 - 100
25+
ValueRange validPercentageRange = ValueRange.of(0, 100);
26+
final Function<Integer, String> message =
27+
input ->
28+
MessageFormat.format(
29+
"Percentage {0} should be in range {1}", input, validPercentageRange.toString());
30+
31+
ThrowingConsumer<Integer> consumer =
32+
input -> {
33+
if (!validPercentageRange.isValidValue(input)) {
34+
throw new ValidationException(message.apply(input));
35+
}
36+
};
37+
38+
if (valid) {
39+
assertDoesNotThrow(() -> consumer.accept(percent));
40+
} else {
41+
assertAll(
42+
() -> {
43+
ValidationException exception =
44+
assertThrows(ValidationException.class, () -> consumer.accept(percent));
45+
assertEquals(exception.getMessage(), message.apply(percent));
46+
});
47+
}
48+
}
49+
50+
@TestFactory
51+
Stream<DynamicTest> testDynamicTestsWithThrowingConsumer() {
52+
// acceptable percentage range: 0 - 100
53+
ValueRange validPercentageRange = ValueRange.of(0, 100);
54+
final Function<Integer, String> message =
55+
input ->
56+
MessageFormat.format(
57+
"Percentage {0} should be in range {1}", input, validPercentageRange.toString());
58+
59+
// Define the ThrowingConsumer that validates the input percentage
60+
ThrowingConsumer<TestCase> consumer =
61+
testCase -> {
62+
if (!validPercentageRange.isValidValue(testCase.percent)) {
63+
throw new ValidationException(message.apply(testCase.percent));
64+
}
65+
};
66+
67+
ThrowingConsumer<TestCase> executable =
68+
testCase -> {
69+
if (testCase.valid) {
70+
assertDoesNotThrow(() -> consumer.accept(testCase));
71+
} else {
72+
assertAll(
73+
() -> {
74+
ValidationException exception =
75+
assertThrows(ValidationException.class, () -> consumer.accept(testCase));
76+
assertEquals(exception.getMessage(), message.apply(testCase.percent));
77+
});
78+
}
79+
};
80+
// Test data: an array of test cases with inputs and their validity
81+
Collection<TestCase> testCases =
82+
Arrays.asList(new TestCase(50, true), new TestCase(130, false), new TestCase(-30, false));
83+
84+
Function<TestCase, String> displayNameGenerator =
85+
testCase -> "Testing percentage: " + testCase.percent;
86+
87+
// Generate dynamic tests
88+
return DynamicTest.stream(testCases.stream(), displayNameGenerator, executable);
89+
}
90+
91+
// Helper record to represent a test case
92+
record TestCase(int percent, boolean valid) {}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package io.reflectoring.functional;
2+
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTimeout;
7+
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
8+
9+
import java.time.Duration;
10+
import java.util.Arrays;
11+
import java.util.Collections;
12+
import java.util.List;
13+
import java.util.concurrent.TimeUnit;
14+
import java.util.function.Consumer;
15+
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.api.function.ThrowingSupplier;
17+
import org.junit.jupiter.params.ParameterizedTest;
18+
import org.junit.jupiter.params.provider.CsvSource;
19+
import org.opentest4j.AssertionFailedError;
20+
21+
public class ThrowingSupplierTest {
22+
private final List<Long> numbers = Arrays.asList(100L, 200L, 50L, 300L);
23+
private final Consumer<List<Long>> checkSorting =
24+
list -> assertEquals(List.of(50L, 100L, 200L, 300L), list);
25+
26+
ThrowingSupplier<List<Long>> sorter =
27+
() -> {
28+
if (numbers == null || numbers.isEmpty() || numbers.contains(null)) {
29+
throw new ValidationException("Invalid input");
30+
}
31+
TimeUnit.SECONDS.sleep(2);
32+
return numbers.stream().sorted().toList();
33+
};
34+
35+
@ParameterizedTest
36+
@CsvSource({"25.0d,5.0d", "36.0d,6.0d", "49.0d,7.0d"})
37+
void testDoesNotThrowWithSupplier(double input, double expected) {
38+
ThrowingSupplier<Double> findSquareRoot =
39+
() -> {
40+
if (input < 0) {
41+
throw new ValidationException("Invalid input");
42+
}
43+
return Math.sqrt(input);
44+
};
45+
assertEquals(expected, assertDoesNotThrow(findSquareRoot));
46+
}
47+
48+
@Test
49+
void testAssertTimeoutWithSupplier() {
50+
// slow execution
51+
assertThrows(AssertionFailedError.class, () -> assertTimeout(Duration.ofSeconds(1), sorter));
52+
53+
// fast execution
54+
assertDoesNotThrow(
55+
() -> {
56+
List<Long> result = assertTimeout(Duration.ofSeconds(5), sorter);
57+
checkSorting.accept(result);
58+
});
59+
60+
// reset the number list and verify if the supplier validates it
61+
Collections.fill(numbers, null);
62+
63+
ValidationException exception =
64+
assertThrows(ValidationException.class, () -> assertTimeout(Duration.ofSeconds(1), sorter));
65+
assertEquals("Invalid input", exception.getMessage());
66+
}
67+
68+
@Test
69+
void testAssertTimeoutPreemptivelyWithSupplier() {
70+
// slow execution
71+
assertThrows(
72+
AssertionFailedError.class, () -> assertTimeoutPreemptively(Duration.ofSeconds(1), sorter));
73+
74+
// fast execution
75+
assertDoesNotThrow(
76+
() -> {
77+
List<Long> result = assertTimeoutPreemptively(Duration.ofSeconds(5), sorter);
78+
checkSorting.accept(result);
79+
});
80+
}
81+
}

0 commit comments

Comments
 (0)