0

Consider I have a method to test, which

  • may have side-effects (like files created on the file system), and
  • may throw an exception.

Some of the side effects can be observed (and tested) even when the exception is thrown. My sample test code is below:

final SoftAssertions softly = new SoftAssertions();

try {
    /*
     * May throw an exception
     */ 
    doSmth();
} catch (final IOException ioe) {
    /*
     * How do I add a soft assertion wrapping an exception?
     */ 
}

/*
 * Testing for side effects.
 */
softly.assertThat(...).as("%s exit code", ...).isEqualTo(0);
softly.assertThat(...).as("the number of downloaded files").isEqualTo(3);
softly.assertThat(...).as("this should be true").isTrue();
softly.assertThat(...).as("and this should be true, too").isTrue();

softly.assertAll();

Question 1

What is the best way to create yet another soft assertion out of the exception thrown? With the raw TestNG API, I could write simply

softly.fail(ioe.toString(), ioe);

but AssertJ doesn't seem to provide anything similar. So far, my best option is adding smth like this to the catch block:

softly.assertThat(true).as(ioe.toString()).isFalse();

Are there any better alternatives?

Question 2

How do I make exception(s) thrown by my code being tested, appear as a cause (or suppressed exceptions) of the resulting AssertionError? Currently, I do the following:

Throwable failure = null;
try {
    doSmth();
} catch (final IOException ioe) {
    failure = ioe;
}

try {
    softly.assertAll();
} catch (final AssertionError ae) {
    if (failure != null) {
        if (ae.getCause() == null) {
            ae.initCause(failure);
        } else {
            ae.addSuppressed(failure);
        }
    }
    throw ae;
}

-- but a more elegant version is much appreciated.

Bass
  • 4,977
  • 2
  • 36
  • 82

2 Answers2

3

for question 1 Xaero suggestion works fine.

For addressing both questions though, try using catchThrowable combined with fail(String failureMessage, Throwable realCause) (or the one for soft assertions).

If you have caught a non null throwable (which means that the code under test did throw an exception) you can then use fail to build an AssertionError with a custom error message and pass the caught exception as the cause of the AssertionError.

The code would look like:

Throwable thrown = catchThrowable(() -> { doSmth(); });

if (thrown != null) {
  softly.fail("boom!", thrown);
} else {
  softly.assertThat(...).as("%s exit code", ...).isZero();
  softly.assertThat(...).as("the number of downloaded files").isEqualTo(3);
  softly.assertThat(...).as("this should be true").isTrue();
  softly.assertThat(...).as("and this should be true, too").isTrue();
}

The code above makes me a bit uncomfortable though because it is testing two different scenarios, one when there is an exception thrown and another when there is none. It might be a good idea to create two test cases which will simplify both the test and the assertions part (I believe).

Anyway, hope it helps!

ps: note that you can use isZero() instead of isEqualTo(0)

Joel Costigliola
  • 6,308
  • 27
  • 35
  • I share your concern with the dual testing scenarios. It's why I couldn't provide an answer I was comfortable with for the second scenario, as there is too much deviation between what's actually being tested, and the subject under question (if that makes sense). – Xaero Degreaz Mar 05 '18 at 01:49
1

Question 1: You can use assertThatThrownBy for this:

softly.assertThatThrownBy(() -> doSmth())
    .isInstanceOf(Exception.class)
    .hasMessage("My Message");
Xaero Degreaz
  • 1,045
  • 11
  • 18
  • Thank you. Reg. the 1st question, I want my soft assertion to eventually fail if an exception is thrown, so `softly.assertThatCode(() -> doSmth()).doesNotThrowAnyException()` is more appropriate. Reg. the 2nd question -- I want to manipulate not the cause of the original failure, but that of the final `AssertionError` thrown from `softly.assertAll();`. – Bass Mar 01 '18 at 15:31
  • I'm sorry, I don't have an answer for that one, but it seems that since you're asking two separate questions, they deserve two separate SO questions as well. It may serve your purpose better since the title of your second question can be more indicative of your problem. – Xaero Degreaz Mar 01 '18 at 20:39
  • 1
    Maybe you can try making your soft assertions instance a spy object, then you can manipulate the return values much like that of a mock. – Xaero Degreaz Mar 23 '18 at 00:06