blob: 8232fb5d8bee67a068a225f2b6f2d885ccf2ce0b [file] [log] [blame] [view]
## 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.