| ## Testing |
| |
| ### Do not rely on mocks |
| |
| All APIs created in Jetpack **must have a testing story**: how developers should |
| write tests for their code that relies on a library, this story should not be |
| "use Mockito to mock class `Foo`". Your goal as an API owner is to **create |
| better alternatives** to mocking. |
| |
| #### Why can't I suggest mocks as testing strategy? |
| |
| Frequently, mocks don't follow guarantees outlined in the API they mock. That |
| leads to: |
| |
| * Significant difference in the behavior that diminishes test value. |
| * Brittle tests, that make hard to evolve both apps and libraries, because new |
| code may start to rely on the guarantees broken in a mock. Let's take a look |
| at a simplified example. So, let's say you mocked a bundle and getString in |
| it: |
| |
| ```java |
| Bundle mock = mock(Bundle.class); |
| when(mock.getString("key")).thenReturn("result"); |
| ``` |
| |
| But you don't mock it to simply call `getString()` in your test. A goal is |
| not to test a mock, the goal is always to test your app code, so your app |
| code always interacts with a mock in some way: |
| |
| ```java |
| Bundle bundle = mock(Bundle.class); |
| when(mock.getString("key")).thenReturn("result"); |
| mycomponent.consume(bundle) |
| ``` |
| |
| Originally the test worked fine, but over time `component.consume` is |
| evolving, and, for example, it may start to call `containsKey` on the given |
| bundle. But our test passes a mock that don't expect such call and, boom, |
| test is broken. However, component code is completely valid and has nothing |
| to do with the broken test. We observed a lot of issues like that during |
| updates of Android SDK and Jetpack libraries to newer versions internally at |
| google. Suggesting to mock our own components is shooting ourselves in the |
| foot, it will make adoption of newer version of libraries even slower. |
| |
| * Messy tests. It always starts with simple mock with one method, but then |
| this mock grows with the project, and as a result test code has sub-optimal |
| half-baked class implementation of on top of the mock. |
| |
| #### But it's okay to mock interfaces, right? |
| |
| It depends. There are interfaces that don't imply any behavior guarantees and |
| they are ok to be mocked. However, **not all** interfaces are like that: for |
| example, `Map` is an interface but it has a lot of contracts required from |
| correct implementation. Examples of interfaces that are ok to mock are callback |
| interfaces in general, for example: `View.OnClickListener`, `Runnable`. |
| |
| #### What about spying? |
| |
| Spying on these classes is banned as well - Mockito spies permit stubbing of |
| methods just like mocks do, and interaction verification is brittle and |
| unnecessary for these classes. Rather than verifying an interaction with a |
| class, developers should observe the result of an interaction - the effect of a |
| task submitted to an `Executor`, or the presence of a fragment added to your |
| layout. If an API in your library misses a way to have such checks, you should |
| add methods to do that. |
| |
| #### Avoid Mockito in your own tests |
| |
| One of the things that would help you to identify if your library is testable |
| without Mockito is not using Mockito yourself. Yes, historically we heavily |
| relied on Mockito ourselves and old tests are not rewritten, but new tests |
| shouldn't follow up that and should take as an example good citizens, for |
| example, `-ktx` modules. These modules don't rely on Mockito and have concise |
| expressive tests. |
| |
| One of the popular and legit patterns for Mockito usage were tests that verify |
| that a simple callback-like interface receives correct parameters. |
| |
| ```java |
| class MyApi { |
| interface Callback { |
| void onFoo(Value value); |
| } |
| void foo() { … } |
| void registerFooCallback(Callback callback) {...} |
| } |
| ``` |
| |
| In API like the one above, in Java 7 tests for value received in `Callback` |
| tended to become very wordy without Mockito. But now in your tests you can use |
| Kotlin and test will be as short as with Mockito: |
| |
| ```kotlin |
| fun test() { |
| var receivedValue = null |
| myApi.registerCallback { value -> receivedValue = value } |
| myApi.foo() |
| // verify receivedValue |
| } |
| ``` |
| |
| #### Don't compromise in API to enable Mockito |
| |
| Mockito on Android |
| [had an issue](https://github.com/Mockito/Mockito/issues/1173) with mocking |
| final classes. Moreover, internally at Google this feature is disabled even for |
| non-Android code. So you may hear complaints that some of your classes are not |
| mockable, however **it is not a reason for open up a class for extension**. What |
| you should instead is verify that is possible to write the same test without |
| mocking, if not, again you should **provide better alternative in your API**. |
| |
| #### How do I approach testing story for my API? |
| |
| The best way is to step into developer's shoes and write a sample app that is a |
| showcase for your API, then go to the next step - test that code also. If you |
| are able to implement tests for your demo app, then users of your API will also |
| be able to implement tests for functionalities where your API is also used. |