Skip to content

Commit

Permalink
Fixes #598 : AbstractSoftAssertions gain errorsCollected() i
Browse files Browse the repository at this point in the history
This will allow subclasses to have access to SoftProxies's ErrorCollector's collected errors, without exposing SoftProxies's internals.
  • Loading branch information
MWFNexient authored and joel-costigliola committed Jan 30, 2016
1 parent 1d32969 commit de90083
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 75 deletions.
Expand Up @@ -12,6 +12,8 @@
*/
package org.assertj.core.api;

import java.util.List;

public class AbstractSoftAssertions {

protected final SoftProxies proxies;
Expand All @@ -23,4 +25,8 @@ public AbstractSoftAssertions() {
protected <T, V> V proxy(Class<V> assertClass, Class<T> actualClass, T actual) {
return proxies.create(assertClass, actualClass, actual);
}
}

protected List<Throwable> errorsCollected(){
return proxies.errorsCollected();
}
}
28 changes: 13 additions & 15 deletions src/main/java/org/assertj/core/api/BDDSoftAssertions.java
Expand Up @@ -34,21 +34,21 @@
* then(mansion.professor()).as(&quot;Professor&quot;).isEqualTo(&quot;well kempt&quot;);
* }</code></pre>
* </p>
*
*
* <p>
* After running the test, JUnit provides us with the following exception message:
* <pre><code class='java'> org.junit.ComparisonFailure: [Living Guests] expected:&lt;[7]&gt; but was:&lt;[6]&gt;</code></pre>
*
*
* <p>
* Oh no! A guest has been murdered! But where, how, and by whom?
* </p>
*
*
* <p>
* Unfortunately frameworks like JUnit halt the test upon the first failed assertion. Therefore, to collect more
* evidence, we'll have to rerun the test (perhaps after attaching a debugger or modifying the test to skip past the
* first assertion). Given that hosting dinner parties takes a long time, this seems rather inefficient.
* </p>
*
*
* <p>
* Instead let's change the test so that at its completion we get the result of all assertions at once. We can do that
* by using a BDDSoftAssertions instance instead of the static methods on {@link BDDAssertions} as follows:
Expand All @@ -66,42 +66,42 @@
* softly.then(mansion.professor()).as(&quot;Professor&quot;).isEqualTo(&quot;well kempt&quot;);
* softly.assertAll();
* } </code></pre>
*
*
* <p>
* Now upon running the test our JUnit exception message is far more detailed:
* <pre><code class='java'> org.assertj.core.api.SoftAssertionError: The following 4 assertions failed:
* 1) [Living Guests] expected:&lt;[7]&gt; but was:&lt;[6]&gt;
* 2) [Library] expected:&lt;'[clean]'&gt; but was:&lt;'[messy]'&gt;
* 3) [Candlestick] expected:&lt;'[pristine]'&gt; but was:&lt;'[bent]'&gt;
* 4) [Professor] expected:&lt;'[well kempt]'&gt; but was:&lt;'[bloodied and disheveled]'&gt;</code></pre>
*
*
* <p>
* Aha! It appears that perhaps the Professor used the candlestick to perform the nefarious deed in the library. We
* should let the police take it from here.
* </p>
*
*
* <p>
* BDDSoftAssertions works by providing you with proxies of the AssertJ assertion objects (those created by
* {@link BDDAssertions}#then...) whose assertion failures are caught and stored. Only when you call
* {@link BDDSoftAssertions#assertAll()} will a {@link SoftAssertionError} be thrown containing the error messages of
* those previously caught assertion failures.
* </p>
*
*
* <p>
* Note that because BDDSoftAssertions is stateful you should use a new instance of BDDSoftAssertions per test method.
* Also, if you forget to call assertAll() at the end of your test, the test <strong>will pass</strong> even if any
* assertion objects threw exceptions (because they're proxied, remember?). So don't forget. You might use
* {@link JUnitBDDSoftAssertions} or {@link AutoCloseableBDDSoftAssertions} to get assertAll() to be called
* automatically.
* </p>
*
*
* <p>
* It is recommended to use {@link AbstractAssert#as(String, Object...)} so that the multiple failed assertions can be
* easily distinguished from one another.
* </p>
*
*
* @author Brian Laframboise
*
*
* @see <a href="http://beust.com/weblog/2012/07/29/reinventing-assertions/">Reinventing assertions</a> for the
* inspiration
*/
Expand All @@ -113,10 +113,8 @@ public class BDDSoftAssertions extends AbstractBDDSoftAssertions {
* @throws SoftAssertionError if any proxied assertion objects threw
*/
public void assertAll() {
List<Throwable> errors = proxies.errorsCollected();
if (!errors.isEmpty()) {
throw new SoftAssertionError(extractProperty("message", String.class).from(errors));
}
List<Throwable> errors = errorsCollected();
if (!errors.isEmpty()) throw new SoftAssertionError(extractProperty("message", String.class).from(errors));
}

}
Expand Up @@ -45,7 +45,7 @@ public Statement apply(final Statement base, Description description) {
@Override
public void evaluate() throws Throwable {
base.evaluate();
MultipleFailureException.assertEmpty(proxies.errorsCollected());
MultipleFailureException.assertEmpty(errorsCollected());
}
};
}
Expand Down
9 changes: 1 addition & 8 deletions src/main/java/org/assertj/core/api/JUnitSoftAssertions.java
Expand Up @@ -12,14 +12,11 @@
*/
package org.assertj.core.api;

import org.assertj.core.util.VisibleForTesting;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;

import java.util.List;

/**
* Same as {@link SoftAssertions}, but with the following differences: <br/>
* First, it's a junit rule, which can be used without having to call {@link SoftAssertions#assertAll() assertAll()},
Expand All @@ -45,13 +42,9 @@ public Statement apply(final Statement base, Description description) {
@Override
public void evaluate() throws Throwable {
base.evaluate();
MultipleFailureException.assertEmpty(proxies.errorsCollected());
MultipleFailureException.assertEmpty(errorsCollected());
}
};
}

@VisibleForTesting List<Throwable> getErrors() {
return proxies.errorsCollected();
}

}
24 changes: 12 additions & 12 deletions src/main/java/org/assertj/core/api/SoftAssertions.java
Expand Up @@ -33,21 +33,21 @@
* assertThat(mansion.colonel()).as(&quot;Colonel&quot;).isEqualTo(&quot;well kempt&quot;);
* assertThat(mansion.professor()).as(&quot;Professor&quot;).isEqualTo(&quot;well kempt&quot;);
* }</code></pre>
*
*
* <p>
* After running the test, JUnit provides us with the following exception message:
* <pre><code class='java'> org.junit.ComparisonFailure: [Living Guests] expected:&lt;[7]&gt; but was:&lt;[6]&gt;</code></pre>
*
*
* <p>
* Oh no! A guest has been murdered! But where, how, and by whom?
* </p>
*
*
* <p>
* Unfortunately frameworks like JUnit halt the test upon the first failed assertion. Therefore, to collect more
* evidence, we'll have to rerun the test (perhaps after attaching a debugger or modifying the test to skip past the
* first assertion). Given that hosting dinner parties takes a long time, this seems rather inefficient.
* </p>
*
*
* <p>
* Instead let's change the test so that at its completion we get the result of all assertions at once. We can do that
* by using a SoftAssertions instance instead of the static methods on {@link Assertions} as follows:
Expand All @@ -65,41 +65,41 @@
* softly.assertThat(mansion.professor()).as(&quot;Professor&quot;).isEqualTo(&quot;well kempt&quot;);
* softly.assertAll();
* }</code></pre>
*
*
* <p>
* Now upon running the test our JUnit exception message is far more detailed:
* <pre><code class='java'> org.assertj.core.api.SoftAssertionError: The following 4 assertions failed:
* 1) [Living Guests] expected:&lt;[7]&gt; but was:&lt;[6]&gt;
* 2) [Library] expected:&lt;'[clean]'&gt; but was:&lt;'[messy]'&gt;
* 3) [Candlestick] expected:&lt;'[pristine]'&gt; but was:&lt;'[bent]'&gt;
* 4) [Professor] expected:&lt;'[well kempt]'&gt; but was:&lt;'[bloodied and disheveled]'&gt;</code></pre>
*
*
* <p>
* Aha! It appears that perhaps the Professor used the candlestick to perform the nefarious deed in the library. We
* should let the police take it from here.
* </p>
*
*
* <p>
* SoftAssertions works by providing you with proxies of the AssertJ assertion objects (those created by
* {@link Assertions}#assertThat...) whose assertion failures are caught and stored. Only when you call
* {@link SoftAssertions#assertAll()} will a {@link SoftAssertionError} be thrown containing the error messages of those
* previously caught assertion failures.
* </p>
*
*
* <p>
* Note that because SoftAssertions is stateful you should use a new instance of SoftAssertions per test method. Also,
* if you forget to call assertAll() at the end of your test, the test <strong>will pass</strong> even if any assertion
* objects threw exceptions (because they're proxied, remember?). So don't forget. You might use
* {@link JUnitSoftAssertions} or {@link AutoCloseableSoftAssertions} to get assertAll() to be called automatically.
* </p>
*
*
* <p>
* It is recommended to use {@link AbstractAssert#as(String, Object...)} so that the multiple failed assertions can be
* easily distinguished from one another.
* </p>
*
*
* @author Brian Laframboise
*
*
* @see <a href="http://beust.com/weblog/2012/07/29/reinventing-assertions/">Reinventing assertions</a> for the
* inspiration
*/
Expand All @@ -111,7 +111,7 @@ public class SoftAssertions extends AbstractStandardSoftAssertions {
* @throws SoftAssertionError if any proxied assertion objects threw
*/
public void assertAll() {
List<Throwable> errors = proxies.errorsCollected();
List<Throwable> errors = errorsCollected();
if (!errors.isEmpty()) {
throw new SoftAssertionError(extractProperty("message", String.class).from(errors));
}
Expand Down
1 change: 0 additions & 1 deletion src/main/java/org/assertj/core/internal/Objects.java
Expand Up @@ -44,7 +44,6 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
Expand Down
Expand Up @@ -18,6 +18,7 @@

import java.util.List;

import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

Expand All @@ -29,25 +30,26 @@ public class JUnitBDDSoftAssertionsFailureTest {

@Test
public void should_report_all_errors() throws Throwable {
try {
softly.then(1).isEqualTo(1);
softly.then(1).isEqualTo(2);
softly.then(Lists.newArrayList(1, 2)).containsOnly(1, 3);
MultipleFailureException.assertEmpty(softly.getErrors());
fail("Should not reach here");
} catch (MultipleFailureException e) {
List<Throwable> failures = e.getFailures();
assertThat(failures).hasSize(2).extracting("message")
.contains("expected:<[2]> but was:<[1]>",
String.format("%n" +
"Expecting:%n" +
" <[1, 2]>%n" +
"to contain only:%n" +
" <[1, 3]>%n" +
"elements not found:%n" +
" <[3]>%n" +
"and elements not expected:%n" +
" <[2]>%n"));
}
try {
softly.then(1).isEqualTo(1);
softly.then(1).isEqualTo(2);
softly.then(Lists.newArrayList(1, 2)).containsOnly(1, 3);
MultipleFailureException.assertEmpty(softly.getErrors());
fail("Should not reach here");
} catch (MultipleFailureException e) {
List<Throwable> failures = e.getFailures();
assertThat(failures).hasSize(2)
.extracting("message")
.contains("expected:<[2]> but was:<[1]>",
format("%n" +
"Expecting:%n" +
" <[1, 2]>%n" +
"to contain only:%n" +
" <[1, 3]>%n" +
"elements not found:%n" +
" <[3]>%n" +
"and elements not expected:%n" +
" <[2]>%n"));
}
}
}
Expand Up @@ -18,13 +18,14 @@

import java.util.List;

import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

public class JUnitSoftAssertionsFailureTest {

//we cannot make it a rule here, because we need to test the failure without this test failing!
//@Rule
// we cannot make it a rule here, because we need to test the failure without this test failing!
// @Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();

@Test
Expand All @@ -33,20 +34,23 @@ public void should_report_all_errors() throws Throwable {
softly.assertThat(1).isEqualTo(1);
softly.assertThat(1).isEqualTo(2);
softly.assertThat(Lists.newArrayList(1, 2)).containsOnly(1, 3);
MultipleFailureException.assertEmpty(softly.getErrors());
MultipleFailureException.assertEmpty(softly.errorsCollected());
fail("Should not reach here");
} catch (MultipleFailureException e) {
List<Throwable> failures = e.getFailures();
assertThat(failures).hasSize(2).extracting("message").contains(String.format("expected:<[2]> but was:<[1]>",
"%n" +
"Expecting:%n" +
" <[1, 2]>%n" +
"to contain only:%n" +
" <[1, 3]>%n" +
"elements not found:%n" +
" <[3]>%n" +
"and elements not expected:%n" +
" <[2]>%n"));

assertThat(failures).hasSize(2)
.extracting("message")
.contains("expected:<[2]> but was:<[1]>",
format("%n" +
"Expecting:%n" +
" <[1, 2]>%n" +
"to contain only:%n" +
" <[1, 3]>%n" +
"elements not found:%n" +
" <[3]>%n" +
"and elements not expected:%n" +
" <[2]>%n"));
}
}
}
@@ -0,0 +1,48 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2012-2015 the original author or authors.
*/
package org.assertj.core.api.abstract_; //Make sure that package-private access is lost

import java.util.List;

import org.assertj.core.api.AbstractStandardSoftAssertions;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* This tests that classes extended from {@link AbstractStandardSoftAssertions} will have access to the list of
* collected errors that the various proxies have collected.
*/
public class SoftAssertionsErrorsCollectedTest{
private final Object objectForTesting = null;
private final TestCollector softly = new TestCollector();

@Test
public void return_empty_list_of_errors() throws Exception{
softly.assertThat(objectForTesting).isNull(); //No errors to collect
assertThat(softly.getErrors()).isEmpty();
}

@Test
public void returns_nonempty_list_of_errors() throws Exception{
softly.assertThat(objectForTesting).isNotNull(); //This should allow something to be collected
assertThat(softly.getErrors()).hasAtLeastOneElementOfType(Throwable.class);
}

private class TestCollector extends AbstractStandardSoftAssertions{
public List<Throwable> getErrors(){
return errorsCollected();
}
}
}

0 comments on commit de90083

Please sign in to comment.