Portal Berita dan Informasi Terbaik di Indonesia

The definitive guide to test doubles on Android — Part 1: Theory | by Phellipe Silva | May, 2022

Mocks, stubs, fakes, dummies, and spies on Android: from theory to (good) practice

Photo by alireza irajinia on Unsplash

Regardless of the technology or product you’re working with, knowing how to use test doubles is fundamental to any automated testing strategy. On Android in particular, using this kind of resource becomes even more important when dealing with the non-instrumented tests in your suite. Essentially, the concept behind test doubles is quite simple, but the great number of namings, definitions, and tools available cause a lot of confusion in the development community, you’ve probably heard something like this:

  • “We need to mock this dependency and everything will work fine” 🙌
  • “Avoid using Mocks!” 😱
  • “Mocks vs Stub?” ⚔️
  • “Prefer using Fakes than Mocks” 🤔

You may believe me or not, but the above sentences can be interpreted differently if we don’t know the correct definition of things. If you’ve never heard about test doubles or want to dig deeper into this subject, this blog post is for you!

Before explaining, I would like to recall some basic characteristics of a good unit test: speed, determinism, and easy configuration. With that in mind we can define test doubles as the following:

Test doubles are lightweight substitutes that override real dependencies needed to test a system or behavior. Test doubles are best applied when they replace slow, non-deterministic, or difficult-to-configure dependencies.

Diagram showing the use of test doubles to replace non-deterministic and slow dependencies with deterministic and fast ones.

The concept of test doubles was created by Gerard Meszaros in the book XUnit Test Patterns: Refactoring Test Code and refined by many other references in the software engineering and testing area. In the tech literature, we can find 5 categories of test doubles: Dummies, Stubs, Fakes, Mocks and Spies. Each one of them with its specific purpose.

The Dummy is the simplest test double. It has the sole purpose of being passed as argument, while not having much relevance to the test itself.

Dummies are often used to fill in mandatory parameters and nothing else. We usually don’t need additional tools to create them, although it’s possible to do so.

A simple example of Dummy utilization in a test

In the example above, dummyNote has the sole purpose of being passed as a parameter and its internal values are not very relevant to the test. Below we can find some examples of variables that can be also considered Dummies:

Other types of dummies generated without any tooling

Dummies usually replace data entities that are difficult to configure. They also help to keep the test code small, clean, and free from external tools for its simplicity. Only use another test double or tooling if it’s really necessary.

The Stub is a test double that provides fixed or pre-configured answers to replace the actual implementation of a dependency.

Stubs are usually generated by tools added to your projects, such as Mockito or Mockk, but they can also be implemented manually. They prevent slow or non-deterministic calls from being made during the test execution.

A simple example of Stub utilization in a test

In the example above, we are using MockK to configure the Stub. In line 9, we demonstrate how this configuration is done when we set up exactly what the notesApiStub dependency needs to respond to when fetchAllNotes() is called:

every { notesApiStub.fetchAllNotes() } returns listOf(note, note)

An alternative to this configuration would be creating your own Stub manually, like the example below:

Manually implemented Stub providing fixed answers

Independently on how your Stub is created, a pre-configured response will be returned immediately, avoiding the call to a real backend. Use Stubs when you need quick, deterministic, pre-configured answers for your test.

The Fake is a test double with a very similar purpose to Stub: providing simple and quick answers to a client who consumes it. The main difference is that the Fake uses a simple and lightweight working implementation under the hoods.

The Fake usually implements the same interface as the dependency it’s replacing. Their main characteristic is to have a lightweight functional implementation and be smarter than the Stubs, not only returning pre-defined and hard-coded responses configured previously. For this reason, the Fakes come closer to the system’s actual behavior compared to other test doubles.

A simple example of Fake utilization in a test

The fact that Fakes use the same contract as the real dependency help us to spot inconsistencies in the class design, also preventing internal details of the dependency from being leaked to the test.

Fakes can also be implemented manually as shown in the example below:

Manually implemented Fake, a little smarter implementation compared to the Stubs

A very famous Fake we can find in the Android world is Room’s in-memory database. Despite making use of an external tool to create it, it can still be considered a Fake as it has a lightweight functional implementation that replaces the real database.

val database = Room.inMemoryDatabaseBuilder(

Fakes are widely used in tests that are at the I/O boundaries of the system, replacing dependencies that normally share states, such as databases and backends.

Use Fakes when you need quick, deterministic answers to your test and when you need to reproduce a more complex response that Stubs wouldn’t be able to handle.

The Mock is a double that aims to verify specific interactions with dependencies during the execution of a test. In other words, Mocks replace dependencies that want to be observed when a system is being tested.

Mocks don’t need to set up a hard-coded response like Stubs, they are used to observe interactions with dependencies.

A simple example of Mock utilization in a test

In the example above, we use MockK to set up a Mock. In it, we need to verify that NoteAnalytics calls the AnalyticsWrapper.logEvent(String, String) method with specific parameters when finishing the NoteAnalytics.trackNewNoteEvent(Enum) call.

verify(exactly = 1) { 
analyticsWrapperMock.logEvent("NewNote", "SuperMarket")

The Mock purpose is to observe and verify an interaction with a dependency, while the Stub/Fake purpose is to simulate the dependency behavior and return predefined values.

Use Mocks when you need to check specific interactions with dependencies, especially if the behavior tested has no concrete return value to assert (method that returns Void or Unit). Avoid Mocking dependencies that have defined return values.

Also avoid using them directly on classes that you have no control over the implementation, such as external libraries, as the contract can change at any time and this can turn into a compilation error on your test in the future. In those cases, try to create Wrappers that encapsulate external dependencies that you have no control over and create Mocks for the Wrappers instead.

In my opinion, the Spy is the most confusing test double of all, as its definition varies between different authorship. Summarizing the original definition from Gerard Meszaros and putting it into my own words:

We can say that Spies have a similar purpose to Mocks, which would be to observe and verify interactions with dependencies during the execution of a test. The difference is that Spies use a functional implementation to operate and they can record more complex states which can be used for later verification or assertion.

A Spy can replace or extend the concrete implementation of a dependency by overriding some methods to record relevant information for the test verification. Regardless of whether the Spy is tool-generated or created manually, by definition, there will always be a working implementation under the hood.

A simple example of Spy utilization in a test

In the example above, we have a Spy that was manually implemented. What will define if the test passed is the state that the spy holds. In this test, we want to verify that a specific analytics event was triggered a certain number of times, in other words, to verify that NoteAnalytics interaction with its dependency occurred in this way. The spy method responsible for that will be the analyticsWrapperSpy.getNewNoteEventsRegistered().

You will find different ways to define Spies during your studies, even Martin Fowler’s own definition is a little abstract to me. To ease this confusion a bit, my recommendation is that you focus that Spies observe interactions, have a lightweight implementation and hold state for future assertions.

Spies should be used when you want to verify that your dependency is in a specific state and you can’t do it simply with a Mock. Doing multiple verifications using a Mock on a single test can be an evidence that you are trying to observe a complex state. You can also use a Spy if you want to make your tests more readable in complicated scenarios (through custom methods that are going to be used in later assertions).

After explaining the concepts, I think you can see why it’s no surprise that people have trouble understanding the test doubles, many nuances define each one of them.

To summarize the explanation, we can divide the five doubles into the following categories:

  • Those that don’t simulate behavior or observe interactions: Dummies.
  • Those that simulate behavior: Stubs and Fakes.
  • Those that observe interactions: Mocks and Spies.
  • Those that don’t have a functional implementation under the hood: Dummies, Stubs, and Mocks.
  • Those that have a functional implementation under the hoods: Fakes and Spies.
Summary of the 5 test doubles

All test doubles can be generated manually or by external tools. The ones that are configured by tools are more likely to couple implementation details of the dependency with the test itself. Using tools like Mockito or Mockk can make tests easy to configure initially, but it can also incur a higher maintenance cost in the future. Manually generated doubles tend to increase the test code base size in the beginning but they also make testing easier to maintain in the long run. Choose your trade-offs wisely 😄

It won’t be rare to see people calling Mocks when they really mean Stubs (or other test doubles). For the sake of simplicity, sometimes all doubles are commonly just called Mocks. That’s because many tools that help creating them have generalized this term.

See the example of MockWebServer, MockK, and Mockito. Regardless of whether the test double is a Mock, a Stub, or a Fake, the name Mock is what usually comes up. One reason for this is that some doubles can take multiple roles, being Mocks and Stubs at the same time. With these cases in mind, it became preferable to call doubles that are more generic or have multiple roles of Mocks rather than creating another nomenclature for them.

Another reason for this generalization is due to the existence of inconsistent definitions that appear in books and articles on the internet:

“Classification between mocks, fakes, and stubs is highly inconsistent across the literature. [1] [2] [3] [4] [5] [6]”

— Taken from Wikipedia, Mock Object

In my opinion, it’s not such a big deal to summarize all the test doubles to Mocks, as this can help with communication and reduce cognitive load on the daily basis. However, bear in mind that it’s very important for anyone who uses this generic term to know that it’s an abstraction and the other definitions exist. You will probably find debates around Mocks vs Fakes on the Internet, so it’s always good to know the correct definition of things.

As stated at the beginning of this article, using test doubles in the right way is of utmost importance for the non-instrumented part of the test pyramid. How you use the test doubles will depend on which testing school you follow:

  • The sociable testing school. Also known as classic school, Detroit style, or Chicago Style.
  • Or the solitary testing school. Also known as mockist style or London style.
Comparison between sociable tests and solitary tests. Taken from: https://martinfowler.com/bliki/UnitTest.html

Maybe you have created tests using one of these schools and don’t even realize it. The main difference between them is the definition of “unit”, which consequently defines what a unit test is. Bellow a small summary of each testing school 👇

For the sociable testing school (Detroit Style), the unit is represented by the behavior, regardless of whether this behavior is composed of more than one class or not. In this case, a unit test can be executed with multiple real classes, as long as they represent a single behavior, the test is fast and can be parallelizable (don’t hold state). The recommendation here is to reduce the usage of test doubles and just use them to replace dependencies that share states, that are too slow, or that are out of your control, eg database, backend, third-party tools.

For the solitary testing school (London Style), the unit is represented by the class. In this case, all dependencies of the class being tested should be replaced with doubles, even if the real ones are quick and easy to configure. This style of testing intends to achieve a greater level of isolation and control.

That’s it! Now that you’ve learned the theory behind using test doubles, it’s time to move on to the second part of this series, where we’ll see a little bit more about the specifics of Android:

If you learned something from this article, leave your claps 👏 So I can know that it was helpful to you. Any feedback or questions, send them on Twitter or in the comments of this post. Thanks!

Leave a Reply

Your email address will not be published.