| /* |
| * Copyright 2015-2022 the original author or authors. |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License v2.0 which |
| * accompanies this distribution and is available at |
| * |
| * https://www.eclipse.org/legal/epl-v20.html |
| */ |
| |
| package org.junit.jupiter.api; |
| |
| import static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains; |
| import static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals; |
| import static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith; |
| import static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError; |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertThrowsExactly; |
| |
| import java.io.IOException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.FutureTask; |
| |
| import org.junit.jupiter.api.function.Executable; |
| import org.opentest4j.AssertionFailedError; |
| |
| /** |
| * Unit tests for JUnit Jupiter {@link Assertions}. |
| * |
| * @since 5.8 |
| */ |
| class AssertThrowsExactlyAssertionsTests { |
| |
| private static final Executable nix = () -> { |
| }; |
| |
| @Test |
| void assertThrowsExactlyTheSpecifiedExceptionClass() { |
| var actual = assertThrowsExactly(EnigmaThrowable.class, (Executable) () -> { |
| throw new EnigmaThrowable(); |
| }); |
| assertNotNull(actual); |
| } |
| |
| @Test |
| void assertThrowsExactlyWithTheExpectedChildException() { |
| try { |
| assertThrowsExactly(RuntimeException.class, (Executable) () -> { |
| throw new Exception(); |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.RuntimeException>"); |
| assertMessageContains(ex, "but was: <java.lang.Exception>"); |
| } |
| } |
| |
| @Test |
| void assertThrowsExactlyWithTheExpectedParentException() { |
| try { |
| assertThrowsExactly(RuntimeException.class, (Executable) () -> { |
| throw new NumberFormatException(); |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.RuntimeException>"); |
| assertMessageContains(ex, "but was: <java.lang.NumberFormatException>"); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithMethodReferenceForNonVoidReturnType() { |
| FutureTask<String> future = new FutureTask<>(() -> { |
| throw new RuntimeException("boom"); |
| }); |
| future.run(); |
| |
| ExecutionException exception = assertThrowsExactly(ExecutionException.class, future::get); |
| assertEquals("boom", exception.getCause().getMessage()); |
| } |
| |
| @Test |
| void assertThrowsWithMethodReferenceForVoidReturnType() { |
| var object = new Object(); |
| IllegalMonitorStateException exception; |
| |
| exception = assertThrowsExactly(IllegalMonitorStateException.class, object::notify); |
| assertNotNull(exception); |
| |
| // Note that Object.wait(...) is an overloaded method with a void return type |
| exception = assertThrowsExactly(IllegalMonitorStateException.class, object::wait); |
| assertNotNull(exception); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsThrowable() { |
| EnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, (Executable) () -> { |
| throw new EnigmaThrowable(); |
| }); |
| assertNotNull(enigmaThrowable); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsThrowableWithMessage() { |
| EnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, (Executable) () -> { |
| throw new EnigmaThrowable(); |
| }, "message"); |
| assertNotNull(enigmaThrowable); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsThrowableWithMessageSupplier() { |
| EnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, (Executable) () -> { |
| throw new EnigmaThrowable(); |
| }, () -> "message"); |
| assertNotNull(enigmaThrowable); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsCheckedException() { |
| IOException exception = assertThrowsExactly(IOException.class, (Executable) () -> { |
| throw new IOException(); |
| }); |
| assertNotNull(exception); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsRuntimeException() { |
| IllegalStateException illegalStateException = assertThrowsExactly(IllegalStateException.class, |
| (Executable) () -> { |
| throw new IllegalStateException(); |
| }); |
| assertNotNull(illegalStateException); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsError() { |
| StackOverflowError stackOverflowError = assertThrowsExactly(StackOverflowError.class, |
| (Executable) AssertionTestUtils::recurseIndefinitely); |
| assertNotNull(stackOverflowError); |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatDoesNotThrowAnException() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, nix); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageEquals(ex, "Expected java.lang.IllegalStateException to be thrown, but nothing was thrown."); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageString() { |
| try { |
| assertThrowsExactly(IOException.class, nix, "Custom message"); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionError ex) { |
| assertMessageEquals(ex, |
| "Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown."); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageSupplier() { |
| try { |
| assertThrowsExactly(IOException.class, nix, () -> "Custom message"); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionError ex) { |
| assertMessageEquals(ex, |
| "Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown."); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsAnUnexpectedException() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, (Executable) () -> { |
| throw new NumberFormatException(); |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.IllegalStateException>"); |
| assertMessageContains(ex, "but was: <java.lang.NumberFormatException>"); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageString() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, (Executable) () -> { |
| throw new NumberFormatException(); |
| }, "Custom message"); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| // Should look something like this: |
| // Custom message ==> Unexpected exception type thrown ==> expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException> |
| assertMessageStartsWith(ex, "Custom message ==> "); |
| assertMessageContains(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.IllegalStateException>"); |
| assertMessageContains(ex, "but was: <java.lang.NumberFormatException>"); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageSupplier() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, (Executable) () -> { |
| throw new NumberFormatException(); |
| }, () -> "Custom message"); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| // Should look something like this: |
| // Custom message ==> Unexpected exception type thrown ==> expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException> |
| assertMessageStartsWith(ex, "Custom message ==> "); |
| assertMessageContains(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.IllegalStateException>"); |
| assertMessageContains(ex, "but was: <java.lang.NumberFormatException>"); |
| } |
| } |
| |
| @Test |
| @SuppressWarnings("serial") |
| void assertThrowsWithExecutableThatThrowsInstanceOfAnonymousInnerClassAsUnexpectedException() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, (Executable) () -> { |
| throw new NumberFormatException() { |
| }; |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.IllegalStateException>"); |
| // As of the time of this writing, the class name of the above anonymous inner |
| // class is org.junit.jupiter.api.AssertThrowsExactlyAssertionsTests$2; however, hard |
| // coding "$2" is fragile. So we just check for the presence of the "$" |
| // appended to this class's name. |
| assertMessageContains(ex, "but was: <" + getClass().getName() + "$"); |
| } |
| } |
| |
| @Test |
| void assertThrowsWithExecutableThatThrowsInstanceOfStaticNestedClassAsUnexpectedException() { |
| try { |
| assertThrowsExactly(IllegalStateException.class, (Executable) () -> { |
| throw new LocalException(); |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| assertMessageContains(ex, "expected: <java.lang.IllegalStateException>"); |
| // The following verifies that the canonical name is used (i.e., "." instead of "$"). |
| assertMessageContains(ex, "but was: <" + LocalException.class.getName().replace("$", ".") + ">"); |
| } |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| void assertThrowsWithExecutableThatThrowsSameExceptionTypeFromDifferentClassLoader() throws Exception { |
| try (EnigmaClassLoader enigmaClassLoader = new EnigmaClassLoader()) { |
| |
| // Load expected exception type from different class loader |
| Class<? extends Throwable> enigmaThrowableClass = (Class<? extends Throwable>) enigmaClassLoader.loadClass( |
| EnigmaThrowable.class.getName()); |
| |
| try { |
| assertThrowsExactly(enigmaThrowableClass, (Executable) () -> { |
| throw new EnigmaThrowable(); |
| }); |
| expectAssertionFailedError(); |
| } |
| catch (AssertionFailedError ex) { |
| // Example Output: |
| // |
| // Unexpected exception type thrown ==> |
| // expected: <org.junit.jupiter.api.EnigmaThrowable@5d3411d> |
| // but was: <org.junit.jupiter.api.EnigmaThrowable@2471cca7> |
| |
| assertMessageStartsWith(ex, "Unexpected exception type thrown ==> "); |
| // The presence of the "@" sign is sufficient to indicate that the hash was |
| // generated to disambiguate between the two identical class names. |
| assertMessageContains(ex, "expected: <org.junit.jupiter.api.EnigmaThrowable@"); |
| assertMessageContains(ex, "but was: <org.junit.jupiter.api.EnigmaThrowable@"); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| |
| @SuppressWarnings("serial") |
| private static class LocalException extends RuntimeException { |
| } |
| |
| private static class EnigmaClassLoader extends URLClassLoader { |
| |
| EnigmaClassLoader() { |
| super(new URL[] { EnigmaClassLoader.class.getProtectionDomain().getCodeSource().getLocation() }, |
| getSystemClassLoader()); |
| } |
| |
| @Override |
| public Class<?> loadClass(String name) throws ClassNotFoundException { |
| return (EnigmaThrowable.class.getName().equals(name) ? findClass(name) : super.loadClass(name)); |
| } |
| |
| } |
| |
| } |