DEV Community

loading...
Cover image for Mock recursive calls with Jest

Mock recursive calls with Jest

vazsonyidl
I am a Software Dev Engineer at One Identity ☀️
・3 min read

Introduction

In the recent cadence there were some problems that had been solved with recursive function calls. It is not a problem at all, but what if you have to write unit test for these functions? Forget the what if, you have to write unit tests for your functions, of course! In the next few sections I would like to share my experience with you, and how you can solve this kind of problems.

This post will show you how to solve this problem with Jest.

What this post is not about

This post is not about those fancy stuffs like memoization, recursive function calls to solve fib(50) with the most optimal way, etc and testing these things. While these are great stuff, I have never met with the requirement to memoize a fibonacci call in production, so these tasks are good for your brain and for learning purposes, but now turn our attention to some real life issues.

A bit nicer solution would be a WebSocket, but .. for the sake of demonstration. :)

Setting up the scene

So, imagine you have to solve some pulling thing collaborating with you backend. For example, there is a generation process in progress and your task is to polling this endpoint as long as the required response has not come yet.

The happy path is: your first call will succeed and you are on your way to make your user happy.

The sad path is: the generation is in progress in the first (or second, ...) call and you have to pull files until you have a satisfying result.

Code example for this
  private getChannelInfo(): Observable<string> {
    const channel_id = this.getChannelId();
    return this.getInfo<InfoResponse>(`/api/_info`, {
        params: {channel_id},
      })
      .pipe(
        delay(1000),
        switchMap(body => body?.completed ? of(body) : this.getChannelInfo()),
      );
Enter fullscreen mode Exit fullscreen mode

As long as the endpoint not responds with a satisfying result, specially with completed, we are fetching again with a one second delay.

The problem

Okay, we set the scene, now we should write test for it. Okay, mock the return value for the getInfo function call. But what if we mocked that once? We should flush that mock, and provide another return value on the second call, for example.

The solution

Maybe there are several hacky workarounds for this, I think Jest provides a really straightforward solution.

it('should pull channel info again', () => {
// ... the rest of your setup
serviceUnderTest.getInfo = jest.fn().mockReturnValueOnce(of(null)).mockReturnValueOnce(of(mock));
// ... your assertions
});
Enter fullscreen mode Exit fullscreen mode

What we can see in this example, when the first function calls happen for the getInfo function, jest steps in and says

  • on the first call, I will return of(null)
  • on the second call, I will return of(mock)

With this chaining of mocks, you can provide multiple return values based on the times of call for your function. With this, you can easily test thoroughly your recursive logic in your service/components.

Addition

There is an option to provide return value for any other calls for the function with the following set-up:

jest.fn().mockReturnValue([]).mockReturnValueOnce(of(null)).mockReturnValueOnce(of(mock));

  • the first and seconds returns as described above
  • any other function call will return with []

I hope this give you a bit of interest with this topic, any comments and responses are welcomed :)

References:
Mock once

Discussion (0)