Dietela: Mock as the Paid Actor

How we mock our way out of testing.

Wulan Mantiri
5 min readMay 24, 2021

This article is written as an individual review assignment for PPL CS UI 2021.

Once we have understood why and when to test, the important question is how to test properly.

Here, we will explore the essence of “mocking” and the experience of mocking common use cases with React Native.

The Essence

Mock Analogy

In the simplest words, a mock serves as a stunt double for the implementation code during testing, hence the nickname test double.

Stunt doubles in real life are used to

  • minimize the risk of injury for the actors so the production of movies runs well, and
  • replace the actors to perform dangerous stunts (that can result in unexpected outcomes), as actors are specifically trained and paid to act.

Based on the above analogy, the actors are the tests, while mocks are used to

  • minimize the risk of messing up with the real data so testing would not interfere with production or other environments, and
  • replace the test dependencies with objects that can be controlled and inspected with the expected outcome.

Technicals on Mocking

Mocking is a technique to isolate the tests by replacing dependencies with controlled objects that mimic the real desired behavior.

Examples of dependencies that can be mock candidates are

  • external service calls (such as API)
  • imported modules
  • database queries
  • pre-conditions on global state or variables

The Experience

For mobile development with React Native, we Dietela team use Jest and custom-made mocks.

Jest Mocks

React, along with its relatives like React Native and Next.js, installs Jest as its default test runner. Jest has a mock function that can (1) override implementation, (2) set returned values, and (3) capture calls.

First, we create a jest environment setup that mocks imported modules from third-party libraries. It can be listed in a single file, __mocks__ directory with module-name.ext naming convention, or the combination of both.

example of mocking async cache storage, native modules, and etc in jestSetup.js
in __mocks__ dir, axios module setup is written in `axios.ts`

The test environment setup will be implemented to all tests by default, but we can still mock inside each specific test file. For instance, our Checkout page will call external API services and navigation hooks (that needed a wrapper container from @react-navigation/native library).

how we get the mocked version of axios and override return values of the navigation library

This is how we utilize those mocks (sections explained based on numbering):

  1. We override the implementation of axios request (for one time only) with retrieveCartApi function that returns a hardcoded success Promise response.

2. We render our Checkout page which initially calls an axios request to get cart data from backend API. We wait for the mocked request function to be called, which indicates if the asynchronous process has been executed, the data is retrieved, and the page has finished rendering.

3. We override the implementation of axios request again with payWithMidtransApi function that will be called if the user presses the “Bayar” button.

4. Since our mocked axios request returns a success Promise, the flow is expected to redirect to the Payment page. Any redirection will call the mockedNavigate function. Therefore, we can check our implementation flow is correct by whether or not the mocked function has captured a call.

Custom-made

Aside from using Jest, we also create our own testing global provider that supplies any global state or variables.

Here lies our test provider code:

TestProvider supplies default user context and all pages available for navigation. The default values can be overridden by params prop in our own render(UI component, route, params) function.

Sample usage can be seen below:

  1. We set cache (a pre-condition) in our mocked cache storage from jest.
  2. We render our ChoosePlan page with an authenticated user by supplying the parameter of an existing user and true isAuthenticated flag to userContext.
  3. We execute the flow until the user can pay by selecting a program and nutritionist. We have overridden the implementation of axios request with a success Promise, therefore we expect to be redirected to Checkout page after the user presses “Bayar”.
  4. Unlike the previous test, we do not mock our navigation function as we have provided mocked navigation wrapper in the TestProvider. Here, we can directly check if the displayed page has changed to Checkout instead.

Key Takeaways

Mock is there to let us test the logic of our implementation, instead of replacing the implementation itself.

Some people may think as if everything can be mocked, including the logic. However, mocks should not defeat the purpose of testing.

Mocks should only be used to control the desired pre-condition and dependencies of our code implementation, so we can test our logic in all cases including those coming from outside of our current code’s scope.

--

--