Skip to content

Commit

Permalink
Add doesNotThrowAnyExceptionExcept to AbstractThrowableAssert
Browse files Browse the repository at this point in the history
Fix #3248
  • Loading branch information
pbacz authored and joel-costigliola committed May 12, 2024
1 parent 881ecaf commit 45523f7
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

import static java.lang.String.format;
import static org.assertj.core.error.ShouldNotHaveThrown.shouldNotHaveThrown;
import static org.assertj.core.error.ShouldNotHaveThrownExcept.shouldNotHaveThrownExcept;

import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.regex.Pattern;

Expand All @@ -37,6 +39,7 @@
* @author Mikhail Mazursky
* @author Jack Gough
* @author Mike Gilchrist
* @author Paweł Baczyński
*/
public abstract class AbstractThrowableAssert<SELF extends AbstractThrowableAssert<SELF, ACTUAL>, ACTUAL extends Throwable>
extends AbstractObjectAssert<SELF, ACTUAL> {
Expand Down Expand Up @@ -843,6 +846,44 @@ public void doesNotThrowAnyException() {
if (actual != null) throw Failures.instance().failure(info, shouldNotHaveThrown(actual));
}

/**
* Verifies that the {@link org.assertj.core.api.ThrowableAssert.ThrowingCallable} didn't raise a throwable
* except matching the provided type(s).
* <p>
* Example:
* <pre><code class='java'> void foo() {
* throw new IllegalArgumentException();
* }
*
* void bar() {
* }
*
* // assertions succeed:
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept(IllegalArgumentException.class);
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept(RuntimeException.class);
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept(IllegalArgumentException.class, IllegalStateException.class);
* assertThatCode(() -&gt; bar()).doesNotThrowAnyExceptionExcept();
*
* // assertions fails:
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept();
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept(NumberFormatException.class);
* assertThatCode(() -&gt; foo()).doesNotThrowAnyExceptionExcept(NumberFormatException.class, IOException.class);</code></pre>
*
* @param ignoredExceptionTypes types allowed to be thrown.
* @throws AssertionError if the actual statement raised a {@code Throwable} with type other than provided one(s).
* @since 3.26.0
*/
@SafeVarargs
public final void doesNotThrowAnyExceptionExcept(Class<? extends Throwable>... ignoredExceptionTypes) {
if (isNotAnInstanceOfAny(ignoredExceptionTypes))
throwAssertionError(shouldNotHaveThrownExcept(actual, ignoredExceptionTypes));
}

private boolean isNotAnInstanceOfAny(Class<? extends Throwable>[] exceptionTypes) {
if (actual == null) return false;
return Arrays.stream(exceptionTypes).noneMatch(ex -> ex.isAssignableFrom(actual.getClass()));
}

/**
* A shortcut for <code>extracting(Throwable::getMessage, as(InstanceOfAssertFactories.STRING))</code> which allows
* to extract a throwable's message and then execute assertions on it.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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-2024 the original author or authors.
*/
package org.assertj.core.error;

import static org.assertj.core.util.Throwables.getStackTrace;

public class ShouldNotHaveThrownExcept extends BasicErrorMessageFactory {

@SafeVarargs
public static ErrorMessageFactory shouldNotHaveThrownExcept(Throwable throwable,
Class<? extends Throwable>... throwableClasses) {
return new ShouldNotHaveThrownExcept(throwable, throwableClasses);
}

private ShouldNotHaveThrownExcept(Throwable throwable, Class<? extends Throwable>[] throwableClasses) {
super("%nExpecting code not to raise a throwable except%n %s%nbut caught%n %s",
throwableClasses, getStackTrace(throwable));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,67 +13,123 @@
package org.assertj.core.api;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.BDDAssertions.then;
import static org.assertj.core.api.BDDAssertions.thenCode;
import static org.assertj.core.error.ShouldNotHaveThrown.shouldNotHaveThrown;
import static org.assertj.core.error.ShouldNotHaveThrownExcept.shouldNotHaveThrownExcept;
import static org.assertj.core.util.AssertionsUtil.expectAssertionError;

import java.io.IOException;

import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Test;

class Assertions_assertThatCode_Test {

@Test
void can_invoke_late_assertion_on_assertThatCode() {
// Given
ThrowingCallable boom = raisingException("boom");
void can_invoke_late_assertion() {
// GIVEN
ThrowingCallable boom = raisingException("boom!");

// WHEN/THEN
thenCode(boom).isInstanceOf(Exception.class)
.hasMessageContaining("boom!");
}

// Then
assertThatCode(boom).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
@Test
void should_fail_when_asserting_no_exception_was_thrown_and_an_exception_was_thrown() {
// GIVEN
Exception exception = new Exception("boom");
ThrowingCallable boom = raisingException(exception);
// WHEN
AssertionError error = expectAssertionError(() -> assertThatCode(boom).doesNotThrowAnyException());
// THEN
then(error).hasMessage(shouldNotHaveThrown(exception).create());
}

@Test
void should_fail_when_asserting_no_exception_raised_but_exception_occurs() {
// Given
void should_fail_when_asserting_no_exception_was_thrown_except_an_empty_list_and_an_exception_was_thrown() {
// GIVEN
Exception exception = new Exception("boom");
ThrowingCallable boom = raisingException(exception);
// WHEN
AssertionError error = expectAssertionError(() -> assertThatCode(boom).doesNotThrowAnyExceptionExcept());
// THEN
then(error).hasMessage(shouldNotHaveThrownExcept(exception).create());
}

// Expect
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> {
// When;
assertThatCode(boom).doesNotThrowAnyException();
}).withMessage(shouldNotHaveThrown(exception).create());
@Test
void should_fail_when_asserting_no_exception_was_thrown_except_some_and_a_non_ignored_exception_was_thrown() {
// GIVEN
Exception exception = new IllegalArgumentException("boom");
ThrowingCallable boom = raisingException(exception);
// WHEN
AssertionError error = expectAssertionError(() -> assertThatCode(boom).doesNotThrowAnyExceptionExcept(IllegalStateException.class,
IOException.class));
// THEN
then(error).hasMessage(shouldNotHaveThrownExcept(exception, IllegalStateException.class, IOException.class).create());
}

@Test
void can_use_description_in_error_message() {
// Given
// GIVEN
ThrowingCallable boom = raisingException("boom");

// Expect
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertThatCode(boom).as("Test")
.doesNotThrowAnyException())
.withMessageStartingWith("[Test]");
// WHEN
AssertionError error = expectAssertionError(() -> assertThatCode(boom).as("Test").doesNotThrowAnyException());
// THEN
then(error).hasMessageStartingWith("[Test]");
}

@Test
void error_message_contains_stacktrace() {
// Given
// GIVEN
Exception exception = new Exception("boom");
ThrowingCallable boom = raisingException(exception);
// WHEN
AssertionError error = expectAssertionError(() -> assertThatCode(boom).doesNotThrowAnyException());
// THEN
then(error).hasMessageContainingAll("java.lang.Exception: boom",
"at org.assertj.core.api.Assertions_assertThatCode_Test.error_message_contains_stacktrace");
}

// Then
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertThatCode(boom).doesNotThrowAnyException())
.withMessageContaining("java.lang.Exception: %s", "boom")
.withMessageContaining("at org.assertj.core.api.Assertions_assertThatCode_Test.error_message_contains_stacktrace");
@Test
void should_succeed_when_asserting_no_exception_was_thrown() {
// GIVEN
ThrowingCallable silent = () -> {};
// WHEN/THEN
thenCode(silent).doesNotThrowAnyException();
}

@Test
void should_succeed_when_asserting_no_exception_raised_and_no_exception_occurs() {
// Given
void should_succeed_when_asserting_no_exception_was_thrown_except_an_empty_list() {
// GIVEN
ThrowingCallable silent = () -> {};
// WHEN/THEN
thenCode(silent).doesNotThrowAnyExceptionExcept();
}

// Then
assertThatCode(silent).doesNotThrowAnyException();
@Test
void should_succeed_when_asserting_no_exception_was_thrown_except_some() {
// GIVEN
ThrowingCallable silent = () -> {};
// WHEN/THEN
thenCode(silent).doesNotThrowAnyExceptionExcept(IOException.class, IllegalStateException.class);
}

@Test
void should_succeed_when_asserting_no_exception_was_thrown_except_one_that_is_an_ignored() {
// GIVEN
ThrowingCallable boom = raisingException(new IllegalArgumentException("boom"));
// WHEN/THEN
thenCode(boom).doesNotThrowAnyExceptionExcept(IOException.class, IllegalArgumentException.class);
}

@Test
void should_succeed_when_asserting_no_exception_was_thrown_except_one_that_inherits_an_ignored_exception() {
// GIVEN
ThrowingCallable boom = raisingException(new IllegalArgumentException("boom"));
// WHEN/THEN
thenCode(boom).doesNotThrowAnyExceptionExcept(RuntimeException.class);
}

private ThrowingCallable raisingException(final String reason) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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-2024 the original author or authors.
*/
package org.assertj.core.error;

import static java.lang.String.format;
import static org.assertj.core.api.BDDAssertions.then;
import static org.assertj.core.error.ShouldNotHaveThrownExcept.shouldNotHaveThrownExcept;
import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION;
import static org.assertj.core.util.Throwables.getStackTrace;

import java.io.IOException;

import org.assertj.core.description.TextDescription;
import org.junit.jupiter.api.Test;

class ShouldNotHaveThrownExcept_create_Test {

@Test
void should_create_error_message_with_stacktrace_of_exception() {
// GIVEN
Exception exception = new Exception("boom");
ErrorMessageFactory factory = shouldNotHaveThrownExcept(exception, IOException.class, IllegalStateException.class);
// WHEN
String message = factory.create(new TextDescription("Test"), STANDARD_REPRESENTATION);
// THEN
then(message).isEqualTo(format("[Test] %n" +
"Expecting code not to raise a throwable except%n" +
" [java.io.IOException, java.lang.IllegalStateException]%n" +
"but caught%n" +
" \"%s\"", getStackTrace(exception)));
}
}

0 comments on commit 45523f7

Please sign in to comment.