Skip to content

Commit

Permalink
Merge pull request #309 from sagaofsilence/sr-junit5-functional-inter…
Browse files Browse the repository at this point in the history
…faces

Sr junit5 functional interfaces
  • Loading branch information
Maiklins authored Jul 12, 2024
2 parents 9c6ceb5 + 12f71f6 commit 67d3508
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ then
build_gradle_module "spring-data/spring-data-jdbc-converter"
build_gradle_module "reactive"
build_gradle_module "junit/assumptions"
build_maven_module "junit/junit5/junit5"
build_maven_module "junit/junit5/functional-interfaces"
build_gradle_module "logging"
build_gradle_module "pact/pact-feign-consumer"

Expand Down
35 changes: 35 additions & 0 deletions junit/junit5/functional-interfaces/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

.vscode

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
41 changes: 41 additions & 0 deletions junit/junit5/functional-interfaces/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.reflectoring</groupId>
<artifactId>junit5-functional-interfaces</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<junit.version>5.9.0</junit.version>
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.reflectoring.functional;

public class ValidationException extends Throwable {
public ValidationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package io.reflectoring.functional;

import static org.junit.jupiter.api.Assertions.*;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.opentest4j.AssertionFailedError;

public class ExecutableTest {
private final List<Long> numbers = Arrays.asList(100L, 200L, 50L, 300L);
final Executable sorter =
() -> {
TimeUnit.SECONDS.sleep(2);
numbers.sort(Long::compareTo);
};
private final Executable checkSorting =
() -> assertEquals(List.of(50L, 100L, 200L, 300L), numbers);
private final Executable noChanges = () -> assertEquals(List.of(100L, 200L, 50L, 300L), numbers);

@ParameterizedTest
@CsvSource({"1,1,2,Hello,H,bye,2,byebye", "4,5,9,Good,Go,Go,-10,", "10,21,31,Team,Tea,Stop,-2,"})
void testAssertAllWithExecutable(
int num1,
int num2,
int sum,
String input,
String prefix,
String arg,
int count,
String result) {
assertAll(
() -> assertEquals(sum, num1 + num2),
() -> assertTrue(input.startsWith(prefix)),
() -> {
if (count < 0) {
assertThrows(
IllegalArgumentException.class,
() -> {
new ArrayList<>(count);
});
} else {
assertEquals(result, arg.repeat(count));
}
});
}

@ParameterizedTest
@CsvSource({"one,0,o", "one,1,n"})
void testAssertDoesNotThrowWithExecutable(String input, int index, char result) {
assertDoesNotThrow(() -> assertEquals(input.charAt(index), result));
}

@Test
void testAssertThrowsWithExecutable() {
List<String> input = Arrays.asList("one", "", "three", null, "five");
final IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> {
for (String value : input) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Got invalid value");
}
// process values
}
});
assertEquals("Got invalid value", exception.getMessage());
}

@Test
void testAssertTimeoutWithExecutable() {
assertAll(
() ->
assertThrows(
AssertionFailedError.class, () -> assertTimeout(Duration.ofSeconds(1), sorter)),
checkSorting);

assertAll(
() -> assertDoesNotThrow(() -> assertTimeout(Duration.ofSeconds(5), sorter)), checkSorting);
}

@Test
void testAssertTimeoutPreemptivelyWithExecutable() {
assertAll(
() ->
assertThrows(
AssertionFailedError.class,
() -> assertTimeoutPreemptively(Duration.ofSeconds(1), sorter)),
noChanges);

assertAll(
() -> assertDoesNotThrow(() -> assertTimeoutPreemptively(Duration.ofSeconds(5), sorter)),
checkSorting);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.reflectoring.functional;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.text.MessageFormat;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

public class ThrowingConsumerTest {
@ParameterizedTest
@CsvSource({"50,true", "130,false", "-30,false"})
void testMethodThatThrowsCheckedException(int percent, boolean valid) {
// acceptable percentage range: 0 - 100
ValueRange validPercentageRange = ValueRange.of(0, 100);
final Function<Integer, String> message =
input ->
MessageFormat.format(
"Percentage {0} should be in range {1}", input, validPercentageRange.toString());

ThrowingConsumer<Integer> consumer =
input -> {
if (!validPercentageRange.isValidValue(input)) {
throw new ValidationException(message.apply(input));
}
};

if (valid) {
assertDoesNotThrow(() -> consumer.accept(percent));
} else {
assertAll(
() -> {
ValidationException exception =
assertThrows(ValidationException.class, () -> consumer.accept(percent));
assertEquals(exception.getMessage(), message.apply(percent));
});
}
}

@TestFactory
Stream<DynamicTest> testDynamicTestsWithThrowingConsumer() {
// acceptable percentage range: 0 - 100
ValueRange validPercentageRange = ValueRange.of(0, 100);
final Function<Integer, String> message =
input ->
MessageFormat.format(
"Percentage {0} should be in range {1}", input, validPercentageRange.toString());

// Define the ThrowingConsumer that validates the input percentage
ThrowingConsumer<TestCase> consumer =
testCase -> {
if (!validPercentageRange.isValidValue(testCase.percent)) {
throw new ValidationException(message.apply(testCase.percent));
}
};

ThrowingConsumer<TestCase> executable =
testCase -> {
if (testCase.valid) {
assertDoesNotThrow(() -> consumer.accept(testCase));
} else {
assertAll(
() -> {
ValidationException exception =
assertThrows(ValidationException.class, () -> consumer.accept(testCase));
assertEquals(exception.getMessage(), message.apply(testCase.percent));
});
}
};
// Test data: an array of test cases with inputs and their validity
Collection<TestCase> testCases =
Arrays.asList(new TestCase(50, true), new TestCase(130, false), new TestCase(-30, false));

Function<TestCase, String> displayNameGenerator =
testCase -> "Testing percentage: " + testCase.percent;

// Generate dynamic tests
return DynamicTest.stream(testCases.stream(), displayNameGenerator, executable);
}

// Helper record to represent a test case
record TestCase(int percent, boolean valid) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package io.reflectoring.functional;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.ThrowingSupplier;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.opentest4j.AssertionFailedError;

public class ThrowingSupplierTest {
private final List<Long> numbers = Arrays.asList(100L, 200L, 50L, 300L);
private final Consumer<List<Long>> checkSorting =
list -> assertEquals(List.of(50L, 100L, 200L, 300L), list);

ThrowingSupplier<List<Long>> sorter =
() -> {
if (numbers == null || numbers.isEmpty() || numbers.contains(null)) {
throw new ValidationException("Invalid input");
}
TimeUnit.SECONDS.sleep(2);
return numbers.stream().sorted().toList();
};

@ParameterizedTest
@CsvSource({"25.0d,5.0d", "36.0d,6.0d", "49.0d,7.0d"})
void testDoesNotThrowWithSupplier(double input, double expected) {
ThrowingSupplier<Double> findSquareRoot =
() -> {
if (input < 0) {
throw new ValidationException("Invalid input");
}
return Math.sqrt(input);
};
assertEquals(expected, assertDoesNotThrow(findSquareRoot));
}

@Test
void testAssertTimeoutWithSupplier() {
// slow execution
assertThrows(AssertionFailedError.class, () -> assertTimeout(Duration.ofSeconds(1), sorter));

// fast execution
assertDoesNotThrow(
() -> {
List<Long> result = assertTimeout(Duration.ofSeconds(5), sorter);
checkSorting.accept(result);
});

// reset the number list and verify if the supplier validates it
Collections.fill(numbers, null);

ValidationException exception =
assertThrows(ValidationException.class, () -> assertTimeout(Duration.ofSeconds(1), sorter));
assertEquals("Invalid input", exception.getMessage());
}

@Test
void testAssertTimeoutPreemptivelyWithSupplier() {
// slow execution
assertThrows(
AssertionFailedError.class, () -> assertTimeoutPreemptively(Duration.ofSeconds(1), sorter));

// fast execution
assertDoesNotThrow(
() -> {
List<Long> result = assertTimeoutPreemptively(Duration.ofSeconds(5), sorter);
checkSorting.accept(result);
});
}
}

0 comments on commit 67d3508

Please sign in to comment.