Skip to content
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

Fix ResultMatchers#hasFailureContaining #1292

Merged
19 changes: 14 additions & 5 deletions src/main/java/org/junit/experimental/results/ResultMatchers.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import static org.hamcrest.CoreMatchers.equalTo;

/**
* Matchers on a PrintableResult, to enable JUnit self-tests.
* For example:
Expand Down Expand Up @@ -34,14 +36,21 @@ public static Matcher<PrintableResult> isSuccessful() {
* Matches if there are {@code count} failures
*/
public static Matcher<PrintableResult> failureCountIs(final int count) {
return failureCount(equalTo(count));
}

/**
* Matches if the number of failures matches {@code countMatcher}
*/
public static Matcher<PrintableResult> failureCount(final Matcher<Integer> countMatcher) {
Copy link
Member

@kcooney kcooney May 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we need an overload that takes a Matcher<Integer>. Can you give an example of where this would be more useful than the version that takes an int?

Also, if we add this, shouldn't the parameter have a type of Matcher<? super Integer> (see http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#arrayWithSize(org.hamcrest.Matcher) for one example in Hamcrest)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing failureCountIs(final int count) only allows to do an equals match, whereas taking a Matcher of Integer allows clients to do any kind of integer matching, eg. <, >, >=, <=.
It came from the original implementation of this PR 9f5dfd8

But since I later changed implementation (69e8a8a), it's not needed for this PR. I've removed it and I'll open a new PR for that, as I think it still makes sense on its own.

Please let me know your thoughts

return new TypeSafeMatcher<PrintableResult>() {
public void describeTo(Description description) {
description.appendText("has " + count + " failures");
description.appendText("has a number of failures matching " + countMatcher);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't see anything wrong with the existing description. In theory, changing the description could break some users of ResultMatchers.failureCountIs(). Is there some motivation for the change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your concern but the original string doesn't work well on failure with greaterThan and similar:

  • Expected: has a value equal to or greater than <2> failures
  • Expected: has a value greater than <2> failures
  • Expected: has a value less than <1> failures

Sounds better:

Expected: has a number of failures matching a value equal to or greater than <2>

But we could have the original failureCountIs override describeTo of the new overloaded version..

}

@Override
public boolean matchesSafely(PrintableResult item) {
return item.failureCount() == count;
return countMatcher.matches(item.failureCount());
}
};
}
Expand All @@ -66,9 +75,9 @@ public void describeTo(Description description) {
* contains {@code string}
*/
public static Matcher<PrintableResult> hasFailureContaining(final String string) {
return new BaseMatcher<PrintableResult>() {
public boolean matches(Object item) {
return item.toString().contains(string);
return new TypeSafeMatcher<PrintableResult>() {
public boolean matchesSafely(PrintableResult item) {
return item.failureCount() > 0 && item.toString().contains(string);
}

public void describeTo(Description description) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
package org.junit.tests.experimental.results;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.junit.experimental.results.PrintableResult;
import org.junit.experimental.results.ResultMatchers;
import org.junit.experimental.theories.Theory;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;

import java.util.ArrayList;
import java.util.Collections;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class ResultMatchersTest {

@Test
public void hasFailuresHasGoodDescription() {
assertThat(ResultMatchers.failureCountIs(3).toString(),
is("has 3 failures"));
is("has a number of failures matching <3>"));
}

@Theory
public void hasFailuresDescriptionReflectsInput(int i) {
assertThat(ResultMatchers.failureCountIs(i).toString(),
containsString("" + i));
}

@Test
public void hasFailureContaining_givenResultWithNoFailures() {
PrintableResult resultWithNoFailures = new PrintableResult(new ArrayList<Failure>());

assertThat(ResultMatchers.hasFailureContaining("").matches(resultWithNoFailures), is(false));
}

@Test
public void hasFailureContaining_givenResultWithOneFailure() {
PrintableResult resultWithOneFailure = new PrintableResult(Collections.singletonList(
new Failure(Description.EMPTY, new RuntimeException("my failure"))));

assertThat(ResultMatchers.hasFailureContaining("my failure").matches(resultWithOneFailure), is(true));
assertThat(ResultMatchers.hasFailureContaining("his failure").matches(resultWithOneFailure), is(false));
}

@Test
public void testFailureCount() {
PrintableResult resultWithOneFailure = new PrintableResult(Collections.singletonList(
new Failure(Description.EMPTY, new RuntimeException("failure 1"))));

assertThat(ResultMatchers.failureCount(equalTo(3)).matches(resultWithOneFailure), is(false));
assertThat(ResultMatchers.failureCount(equalTo(1)).matches(resultWithOneFailure), is(true));
}
}