• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014 Google, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.common.truth;
17 
18 import static com.google.common.base.Preconditions.checkNotNull;
19 
20 import org.jspecify.annotations.Nullable;
21 
22 /**
23  * Propositions for {@link Throwable} subjects.
24  *
25  * <p>Truth does not provide its own support for calling a method and automatically catching an
26  * expected exception, only for asserting on the exception after it has been caught. To catch the
27  * exception, we suggest {@link org.junit.Assert#assertThrows(Class,
28  * org.junit.function.ThrowingRunnable) assertThrows} (JUnit), <a
29  * href="https://kotlinlang.org/api/latest/kotlin.test/kotlin.test/assert-fails-with.html">{@code
30  * assertFailsWith}</a> ({@code kotlin.test}), or similar functionality from your testing library of
31  * choice.
32  *
33  * <pre>
34  * InvocationTargetException expected =
35  *     assertThrows(InvocationTargetException.class, () -> method.invoke(null));
36  * assertThat(expected).hasCauseThat().isInstanceOf(IOException.class);
37  * </pre>
38  *
39  * @author Kurt Alfred Kluever
40  */
41 public class ThrowableSubject extends Subject {
42   private final @Nullable Throwable actual;
43 
44   /**
45    * Constructor for use by subclasses. If you want to create an instance of this class itself, call
46    * {@link Subject#check(String, Object...) check(...)}{@code .that(actual)}.
47    */
ThrowableSubject(FailureMetadata metadata, @Nullable Throwable throwable)48   protected ThrowableSubject(FailureMetadata metadata, @Nullable Throwable throwable) {
49     this(metadata, throwable, null);
50   }
51 
ThrowableSubject( FailureMetadata metadata, @Nullable Throwable throwable, @Nullable String typeDescription)52   ThrowableSubject(
53       FailureMetadata metadata, @Nullable Throwable throwable, @Nullable String typeDescription) {
54     super(metadata, throwable, typeDescription);
55     this.actual = throwable;
56   }
57 
58   /*
59    * TODO(cpovirk): consider a special case for isEqualTo and isSameInstanceAs that adds |expected|
60    * as a suppressed exception
61    */
62 
63   /** Returns a {@code StringSubject} to make assertions about the throwable's message. */
hasMessageThat()64   public final StringSubject hasMessageThat() {
65     StandardSubjectBuilder check = check("getMessage()");
66     if (actual instanceof ErrorWithFacts && ((ErrorWithFacts) actual).facts().size() > 1) {
67       check =
68           check.withMessage(
69               "(Note from Truth: When possible, instead of asserting on the full message, assert"
70                   + " about individual facts by using ExpectFailure.assertThat.)");
71     }
72     return check.that(checkNotNull(actual).getMessage());
73   }
74 
75   /**
76    * Returns a new {@code ThrowableSubject} that supports assertions on this throwable's direct
77    * cause. This method can be invoked repeatedly (e.g. {@code
78    * assertThat(e).hasCauseThat().hasCauseThat()....} to assert on a particular indirect cause.
79    */
80   // Any Throwable is fine, and we use plain Throwable to emphasize that it's not used "for real."
81   @SuppressWarnings("ShouldNotSubclass")
hasCauseThat()82   public final ThrowableSubject hasCauseThat() {
83     // provides a more helpful error message if hasCauseThat() methods are chained too deep
84     // e.g. assertThat(new Exception()).hCT().hCT()....
85     // TODO(diamondm) in keeping with other subjects' behavior this should still NPE if the subject
86     // *itself* is null, since there's no context to lose. See also b/37645583
87     if (actual == null) {
88       check("getCause()")
89           .withMessage("Causal chain is not deep enough - add a .isNotNull() check?")
90           .fail();
91       return ignoreCheck()
92           .that(
93               new Throwable() {
94                 @Override
95                 @SuppressWarnings("UnsynchronizedOverridesSynchronized")
96                 public Throwable fillInStackTrace() {
97                   setStackTrace(new StackTraceElement[0]); // for old versions of Android
98                   return this;
99                 }
100               });
101     }
102     return check("getCause()").that(actual.getCause());
103   }
104 }
105