001/* 002 * Copyright 2015 SirWellington Tech. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package tech.sirwellington.alchemy.test.junit; 017 018import tech.sirwellington.alchemy.annotations.designs.FluidAPIDesign; 019 020import static org.hamcrest.Matchers.*; 021import static org.junit.Assert.assertThat; 022import static tech.sirwellington.alchemy.test.Checks.Internal.checkNotNull; 023 024/** 025 * Makes it easier syntactically using Java 8 to assert an Exception is thrown by a section of code. 026 * You can also perform additional verification on the exception that is thrown. 027 * <p> 028 * Example: 029 * <p> 030 * <pre> 031 * {@code 032 * assertThrows(() -> someFunctionThatThrows()) 033 * .isIntanceOf(RuntimeException.class) 034 * .hasNoCause(); 035 * } 036 * </pre> 037 * 038 * @author SirWellington 039 */ 040@FluidAPIDesign 041public final class ThrowableAssertion 042{ 043 044 /** 045 * Assert that a function throws an exception. 046 * 047 * @param operation The Lambda function that encapsulates code you expect to throw an exception. 048 * @return 049 * @throws ExceptionNotThrownException If no exception is thrown. 050 */ 051 public static ThrowableAssertion assertThrows(ExceptionOperation operation) throws ExceptionNotThrownException 052 { 053 checkNotNull(operation, "missing operation"); 054 return new ThrowableAssertion(operation) 055 .execute(); 056 } 057 058 private Throwable caught; 059 private final ExceptionOperation operation; 060 061 private ThrowableAssertion(ExceptionOperation operation) 062 { 063 this.operation = operation; 064 } 065 066 private ThrowableAssertion execute() throws ExceptionNotThrownException 067 { 068 try 069 { 070 operation.call(); 071 } 072 catch (Throwable ex) 073 { 074 this.caught = ex; 075 return this; 076 } 077 throw new ExceptionNotThrownException("Expected an exception"); 078 } 079 080 /** 081 * Check that the Exception is of a particular type. 082 * 083 * @param exceptionClass The expected type of the Exception. 084 * @return 085 */ 086 public ThrowableAssertion isInstanceOf(Class<? extends Throwable> exceptionClass) 087 { 088 assertThat(caught, isA((Class<Throwable>) exceptionClass)); 089 return this; 090 } 091 092 /** 093 * Checks to make sure the exception contains a certain message. 094 * 095 * @param expectedMessage The exact message expected 096 * @return 097 */ 098 public ThrowableAssertion hasMessage(String expectedMessage) 099 { 100 assertThat(caught.getMessage(), is(expectedMessage)); 101 return this; 102 } 103 104 /** 105 * Assert that the exception contains a string in its message. 106 * 107 * @param messageString The partial message to expected. 108 * @return 109 */ 110 public ThrowableAssertion containsInMessage(String messageString) 111 { 112 assertThat(caught.getMessage(), containsString(messageString)); 113 return this; 114 } 115 116 /** 117 * Assert that the exception has no causing exception 118 * 119 * @return 120 */ 121 public ThrowableAssertion hasNoCause() 122 { 123 assertThat(caught.getCause(), nullValue()); 124 return this; 125 } 126 127 /** 128 * Asserts that the Exception has a cause of a particular type. 129 * 130 * @param exceptionClass The type expected. 131 * @return 132 */ 133 public ThrowableAssertion hasCauseInstanceOf(Class<? extends Throwable> exceptionClass) 134 { 135 assertThat(caught.getCause(), notNullValue()); 136 assertThat(caught.getCause(), isA((Class<Throwable>) exceptionClass)); 137 return this; 138 } 139}