From 3019f1bee22f9666f89ce286a1a1facc3c8eba7f Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Thu, 29 Aug 2024 00:32:10 +0200 Subject: [PATCH] Optional matchers (#421) --- .../src/main/java/org/hamcrest/Matchers.java | 44 ++++++++ .../org/hamcrest/optional/OptionalEmpty.java | 36 ++++++ .../hamcrest/optional/OptionalWithValue.java | 73 +++++++++++++ .../optional/OptionalMatchersTest.java | 103 ++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 hamcrest/src/main/java/org/hamcrest/optional/OptionalEmpty.java create mode 100644 hamcrest/src/main/java/org/hamcrest/optional/OptionalWithValue.java create mode 100644 hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java diff --git a/hamcrest/src/main/java/org/hamcrest/Matchers.java b/hamcrest/src/main/java/org/hamcrest/Matchers.java index 85348ea0..9768333a 100644 --- a/hamcrest/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest/src/main/java/org/hamcrest/Matchers.java @@ -3,8 +3,11 @@ import org.hamcrest.collection.ArrayMatching; import org.hamcrest.core.IsIterableContaining; import org.hamcrest.core.StringRegularExpression; +import org.hamcrest.optional.OptionalEmpty; +import org.hamcrest.optional.OptionalWithValue; import org.hamcrest.text.IsEqualCompressingWhiteSpace; +import java.util.Optional; import java.util.regex.Pattern; /** @@ -2184,4 +2187,45 @@ public static org.hamcrest.Matcher hasXPath(java.lang.String x return org.hamcrest.xml.HasXPath.hasXPath(xPath, namespaceContext); } + /** + * Matcher that expects empty {@link Optional}. + * + * @param type of optional value + * @return The matcher. + */ + public static Matcher> emptyOptional() { + return OptionalEmpty.emptyOptional(); + } + + /** + * Matcher for {@link Optional} that expects that value is present. + * + * @param type of optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue() { + return OptionalWithValue.optionalWithValue(); + } + + /** + * Matcher for {@link Optional} that expects that value is present and is equal to value + * + * @param type of optional value + * @param value to validate present optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue(T value) { + return OptionalWithValue.optionalWithValue(value); + } + + /** + * Matcher for {@link Optional} that expects that value is present and matches matcher + * + * @param type of optional value + * @param matcher matcher to validate present optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue(Matcher matcher) { + return OptionalWithValue.optionalWithValue(matcher); + } } diff --git a/hamcrest/src/main/java/org/hamcrest/optional/OptionalEmpty.java b/hamcrest/src/main/java/org/hamcrest/optional/OptionalEmpty.java new file mode 100644 index 00000000..97592fe3 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/optional/OptionalEmpty.java @@ -0,0 +1,36 @@ +package org.hamcrest.optional; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.Optional; + +/** + * Matcher that expects empty {@link Optional}. + + * @param type of {@link Optional} value + */ +public class OptionalEmpty extends TypeSafeDiagnosingMatcher> { + + /** + * Matcher that expects empty {@link Optional}. + * + * @param type of optional value + * @return The matcher. + */ + public static Matcher> emptyOptional() { + return new OptionalEmpty<>(); + } + + @Override + protected boolean matchesSafely(Optional value, Description mismatchDescription) { + mismatchDescription.appendText("is " + value); + return !value.isPresent(); + } + + @Override + public void describeTo(Description description) { + description.appendText("empty"); + } +} diff --git a/hamcrest/src/main/java/org/hamcrest/optional/OptionalWithValue.java b/hamcrest/src/main/java/org/hamcrest/optional/OptionalWithValue.java new file mode 100644 index 00000000..ebd571d1 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/optional/OptionalWithValue.java @@ -0,0 +1,73 @@ +package org.hamcrest.optional; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.hamcrest.core.IsAnything; + +import java.util.Optional; + +import static org.hamcrest.core.IsEqual.equalTo; + +/** + * Matcher for {@link Optional} that expects that value is present. + * + * @param type of {@link Optional} value + */ +public class OptionalWithValue extends TypeSafeDiagnosingMatcher> { + + private final Matcher matcher; + + /** + * Constructor. + * + * @param matcher matcher to validate present optional value + */ + public OptionalWithValue(Matcher matcher) { + this.matcher = matcher; + } + + /** + * Matcher for {@link Optional} that expects that value is present. + * + * @param type of optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue() { + return new OptionalWithValue<>(IsAnything.anything("any")); + } + + /** + * Matcher for {@link Optional} that expects that value is present and is equal to value + * + * @param type of optional value + * @param value to validate present optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue(T value) { + return new OptionalWithValue<>(equalTo(value)); + } + + /** + * Matcher for {@link Optional} that expects that value is present and matches matcher + * + * @param type of optional value + * @param matcher matcher to validate present optional value + * @return The matcher. + */ + public static Matcher> optionalWithValue(Matcher matcher) { + return new OptionalWithValue<>(matcher); + } + + @Override + protected boolean matchesSafely(Optional value, Description mismatchDescription) { + mismatchDescription.appendText("is " + value); + return value.isPresent() && matcher.matches(value.get()); + } + + @Override + public void describeTo(Description description) { + description.appendText("present and matches ") + .appendDescriptionOf(matcher); + } +} diff --git a/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java b/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java new file mode 100644 index 00000000..52b08564 --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java @@ -0,0 +1,103 @@ +package org.hamcrest.optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.optional.OptionalEmpty.emptyOptional; +import static org.hamcrest.optional.OptionalWithValue.optionalWithValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.Optional; + +import org.junit.Test; + +public class OptionalMatchersTest { + + @Test + public void checkEmptyOptional() { + assertThat(Optional.empty(), is(emptyOptional())); + assertThat(Optional.of(1), not(emptyOptional())); + } + + @Test + public void checkEmptyOptionalFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.of(1), emptyOptional()); + }); + assertEquals("\n" + + "Expected: empty\n" + + " but: is Optional[1]", failure.getMessage()); + } + + @Test + public void checkEmptyOptionalIsFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.of(1), is(emptyOptional())); + }); + assertEquals("\n" + + "Expected: is empty\n" + + " but: is Optional[1]", failure.getMessage()); + } + + @Test + public void checkEmptyOptionalIsNotFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.empty(), is(not(emptyOptional()))); + }); + assertEquals("\n" + + "Expected: is not empty\n" + + " but: was ", failure.getMessage()); + } + + @Test + public void checkWithValue() { + assertThat(Optional.empty(), not(optionalWithValue())); + assertThat(Optional.of(1), is(optionalWithValue())); + } + + @Test + public void checkWithMatchingValue() { + assertThat(Optional.empty(), not(optionalWithValue(equalTo(1)))); + assertThat(Optional.of(1), is(optionalWithValue(equalTo(1)))); + assertThat(Optional.of(1), not(optionalWithValue(equalTo(1L)))); + } + + @Test + public void checkWithLiteralValue() { + assertThat(Optional.empty(), not(optionalWithValue(1))); + assertThat(Optional.of(1), is(optionalWithValue(1))); + assertThat(Optional.of(1), not(optionalWithValue(1L))); + } + + @Test + public void checkWithValueFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.empty(), is(optionalWithValue())); + }); + assertEquals("\n" + + "Expected: is present and matches any\n" + + " but: is Optional.empty", failure.getMessage()); + } + + @Test + public void checkWithMatchingValueFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.empty(), is(optionalWithValue(equalTo(1)))); + }); + assertEquals("\n" + + "Expected: is present and matches <1>\n" + + " but: is Optional.empty", failure.getMessage()); + } + + @Test + public void checkWithLiteralValueFailure() { + AssertionError failure = assertThrows(AssertionError.class, () -> { + assertThat(Optional.of("text"), is(optionalWithValue("Hello, world"))); + }); + assertEquals("\n" + + "Expected: is present and matches \"Hello, world\"\n" + + " but: is Optional[text]", failure.getMessage()); + } +}