Skip to content

Commit

Permalink
Optional matchers (hamcrest#421)
Browse files Browse the repository at this point in the history
  • Loading branch information
seregamorph authored Aug 28, 2024
1 parent 6bb90de commit 3019f1b
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 0 deletions.
44 changes: 44 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/Matchers.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -2184,4 +2187,45 @@ public static org.hamcrest.Matcher<org.w3c.dom.Node> hasXPath(java.lang.String x
return org.hamcrest.xml.HasXPath.hasXPath(xPath, namespaceContext);
}

/**
* Matcher that expects empty {@link Optional}.
*
* @param <T> type of optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> emptyOptional() {
return OptionalEmpty.emptyOptional();
}

/**
* Matcher for {@link Optional} that expects that value is present.
*
* @param <T> type of optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue() {
return OptionalWithValue.optionalWithValue();
}

/**
* Matcher for {@link Optional} that expects that value is present and is equal to <code>value</code>
*
* @param <T> type of optional value
* @param value to validate present optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue(T value) {
return OptionalWithValue.optionalWithValue(value);
}

/**
* Matcher for {@link Optional} that expects that value is present and matches <code>matcher</code>
*
* @param <T> type of optional value
* @param matcher matcher to validate present optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue(Matcher<? super T> matcher) {
return OptionalWithValue.optionalWithValue(matcher);
}
}
36 changes: 36 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/optional/OptionalEmpty.java
Original file line number Diff line number Diff line change
@@ -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 <T> type of {@link Optional} value
*/
public class OptionalEmpty<T> extends TypeSafeDiagnosingMatcher<Optional<T>> {

/**
* Matcher that expects empty {@link Optional}.
*
* @param <T> type of optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> emptyOptional() {
return new OptionalEmpty<>();
}

@Override
protected boolean matchesSafely(Optional<T> value, Description mismatchDescription) {
mismatchDescription.appendText("is " + value);
return !value.isPresent();
}

@Override
public void describeTo(Description description) {
description.appendText("empty");
}
}
Original file line number Diff line number Diff line change
@@ -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 <T> type of {@link Optional} value
*/
public class OptionalWithValue<T> extends TypeSafeDiagnosingMatcher<Optional<T>> {

private final Matcher<? super T> matcher;

/**
* Constructor.
*
* @param matcher matcher to validate present optional value
*/
public OptionalWithValue(Matcher<? super T> matcher) {
this.matcher = matcher;
}

/**
* Matcher for {@link Optional} that expects that value is present.
*
* @param <T> type of optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue() {
return new OptionalWithValue<>(IsAnything.anything("any"));
}

/**
* Matcher for {@link Optional} that expects that value is present and is equal to <code>value</code>
*
* @param <T> type of optional value
* @param value to validate present optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue(T value) {
return new OptionalWithValue<>(equalTo(value));
}

/**
* Matcher for {@link Optional} that expects that value is present and matches <code>matcher</code>
*
* @param <T> type of optional value
* @param matcher matcher to validate present optional value
* @return The matcher.
*/
public static <T> Matcher<Optional<T>> optionalWithValue(Matcher<? super T> matcher) {
return new OptionalWithValue<>(matcher);
}

@Override
protected boolean matchesSafely(Optional<T> 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);
}
}
103 changes: 103 additions & 0 deletions hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java
Original file line number Diff line number Diff line change
@@ -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 <Optional.empty>", 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());
}
}

0 comments on commit 3019f1b

Please sign in to comment.