1 package com.android.onboarding.testing
2
3 import com.google.common.truth.Fact.fact
4 import com.google.common.truth.Fact.simpleFact
5 import com.google.common.truth.FailureMetadata
6 import com.google.common.truth.StandardSubjectBuilder
7 import com.google.common.truth.Subject
8 import com.google.common.truth.ThrowableSubject
9 import com.google.common.truth.Truth
10 import com.google.common.truth.Truth.assertAbout
11 import com.google.common.truth.Truth.assertThat
12 import com.google.errorprone.annotations.CanIgnoreReturnValue
13 import kotlin.reflect.KClass
14
15 /** A [Truth] [Subject] to assert expected [Throwable]s being thrown. */
16 class ErrorSubject
17 private constructor(metadata: FailureMetadata, private val actual: (() -> Any?)?) :
18 Subject(metadata, actual) {
19
20 @CanIgnoreReturnValue
failsWithnull21 inline fun <reified T : Throwable> failsWith(): ThrowableSubject = failsWith(T::class)
22
23 @CanIgnoreReturnValue
24 fun <T : Throwable> failsWith(expected: KClass<T>): ThrowableSubject {
25 if (actual == null) {
26 failWithoutActual(
27 fact("expected to fail with", expected.qualifiedName),
28 simpleFact("but no action was provided"),
29 )
30 return assertThat(null as Throwable?)
31 }
32 val error = runCatching(actual).exceptionOrNull()
33 if (error == null) {
34 failWithoutActual(
35 fact("expected to fail with", expected.qualifiedName),
36 simpleFact("but did not fail"),
37 )
38 } else if (!expected.java.isInstance(error)) {
39 failWithoutActual(
40 fact("expected to fail with", expected.qualifiedName),
41 fact("but failed with", error::class.qualifiedName),
42 )
43 }
44 return assertThat(error)
45 }
46
47 companion object : Factory<ErrorSubject, () -> Any?> {
createSubjectnull48 override fun createSubject(metadata: FailureMetadata, actual: (() -> Any?)?): ErrorSubject =
49 ErrorSubject(metadata, actual)
50 }
51 }
52
53 /** @see assertFailsWith */
54 inline fun <reified T : Throwable> StandardSubjectBuilder.failsWith(noinline action: () -> Any?) {
55 about(ErrorSubject).that(action).failsWith<T>()
56 }
57
58 /**
59 * Asserts that the given [action] fails with the expected [Throwable] [T]
60 *
61 * Unfortunately @[CanIgnoreReturnValue] is not supported on inline library functions and as such
62 * this utility function is not returning [ThrowableSubject] for further assertions. If you need
63 * further assertions consider using one of the more verbose forms:
64 * ```
65 * @get:Rule val expect: Expect = Expect.create()
66 * expect.about(ErrorSubject).that { functionUnderTest() }.failsWith<Throwable>().isNotNull()
67 *
68 * assertAbout(ErrorSubject).that { functionUnderTest() }.failsWith<Throwable>().isNotNull()
69 * ```
70 */
assertFailsWithnull71 inline fun <reified T : Throwable> assertFailsWith(noinline action: () -> Any?) {
72 assertAbout(ErrorSubject).that(action).failsWith<T>()
73 }
74