1# Adding custom Truth subjects 2 3[TOC] 4 5## Custom truth subjects 6 7Every subject class should extend 8[Subject](https://truth.dev/api/latest/com/google/common/truth/Subject.html) and 9follow the naming schema `[ClassUnderTest]Subject`. The Subject must also have a 10constructor that accepts 11[FailureMetadata](https://truth.dev/api/latest/com/google/common/truth/FailureMetadata.html) 12and a reference to the object under test, which are both passed to the 13superclass. 14 15```kotlin 16class NavControllerSubject private constructor( 17 metadata: FailureMetadata, 18 private val actual: NavController 19) : Subject(metadata, actual) { } 20``` 21 22### Subject factory 23 24The Subject class should also contain two static fields; a 25[Subject Factory](https://truth.dev/api/latest/com/google/common/truth/Subject.Factory.html) 26and an`assertThat()` shortcut method. 27 28A subject Factory provides most of the functionality of the Subject by allowing 29users to perform all operations provided in the Subject class by passing this 30factory to `about()` methods. E.g: 31 32`assertAbout(navControllers()).that(navController).isGraph(x)` where `isGraph()` 33is a method defined in the Subject class. 34 35The assertThat() shortcut method simply provides a shorthand method for making 36assertions about the Subject without needing a reference to the subject factory. 37i.e., rather than using 38`assertAbout(navControllers()).that(navController).isGraph(x)` users can simply 39use`assertThat(navController).isGraph(x)`. 40 41```kotlin 42companion object { 43 fun navControllers(): Factory<NavControllerSubject, NavController> = 44 Factory<NavControllerSubject, NavController> { metadata, actual -> 45 NavControllerSubject(metadata, actual) 46 } 47 48 @JvmStatic 49 fun assertThat(actual: NavController): NavControllerSubject { 50 return assertAbout(navControllers()).that(actual) 51 } 52} 53``` 54 55### Assertion methods 56 57When creating assertion methods for your custom Subject the names of these 58methods should follow the 59[Truth naming convention](https://truth.dev/faq#assertion-naming). 60 61To create assertion methods it is necessary to either delegate to an existing 62assertion method by using `Subject.check()` or to provide your own failure 63strategy by using`failWithActual()` or `failWithoutActual()`. 64 65When using `failWithActual()` the error message will display the`toString()` 66value of the Subject object. Additional information can be added to these error 67messages by using `fact(key, value)` or `simpleFact(value)` where `fact()` will 68be output as a colon separated key, value pair. 69 70```kotlin 71fun isGraph(@IdRes navGraph: Int) { 72 check("graph()").that(actual.graph.id).isEqualTo(navGraph) 73} 74 75// Sample Failure Message 76value of : navController.graph() 77expected : 29340 78but was : 10394 79navController was : {actual.toString() value} 80``` 81 82```kotlin 83fun isGraph(@IdRes navGraph: Int) { 84 val actualGraph = actual.graph.id 85 if (actualGraph != navGraph) { 86 failWithoutActual( 87 fact("expected id", navGraph.toString()), 88 fact("but was", actualGraph.toString()), 89 fact("current graph is", actual.graph) 90 ) 91 } 92} 93 94// Sample Failure Message 95expected id : 29340 96but was : 10394 97current graph is : {actual.graph.toString() value} 98``` 99 100## Testing 101 102When testing expected successful assertions it is enough to simply call the 103assertion with verified successful actual and expected values. 104 105```kotlin 106private lateinit var navController: NavController 107@Before 108fun setUp() { 109 navController = NavController( 110 ApplicationProvider.getApplicationContext() 111 ).apply { 112 navigationProvider += TestNavigator() 113 // R.navigation.testGraph == R.id.test_graph 114 setGraph(R.navigation.test_graph) 115 } 116} 117 118@Test 119fun testGraph() { 120 assertThat(navController).isGraph(R.id.test_graph) 121} 122``` 123 124To test that expected failure cases you should use the 125[assertThrows](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt) 126function from the AndroidX testutils module. The assertions.kt file contains two 127assertThrows functions. The second method, which specifically returns a 128TruthFailureSubject, should be used since it allows for validating additional 129information about the FailureSubject, particularly that it contains specific 130fact messages. 131 132```kotlin 133@Test 134fun testGraphFailure() { 135 with(assertThrows { 136 assertThat(navController).isGraph(R.id.second_test_graph) 137 }) { 138 factValue("expected id").isEqualTo(R.id.second_test_graph.toString()) 139 factValue("but was").isEqualTo(navController.graph.id.toString()) 140 factValue("current graph is").isEqualTo(navController.graph.toString()) 141 } 142} 143``` 144 145## Helpful resources 146 147[Truth extension points](https://truth.dev/extension.html) 148