How to mock an asynchronous function call in another class How to mock an asynchronous function call in another class reactjs reactjs

How to mock an asynchronous function call in another class


Sometimes, when a test is hard to write, it is trying to tell us that we have a design problem.

I think a small refactor could make things a lot easier - make SalesService a collaborator instead of an internal.

By that I mean, instead of calling new SalesService() inside your component, accept the sales service as a prop by the calling code. If you do that, then the calling code can also be your test, in which case all you need to do is mock the SalesService itself, and return whatever you want (using sinon or any other mocking library, or even just creating a hand rolled stub).


You could potentially abstract the new keyword away using a SalesService.create() method, then use jest.spyOn(object, methodName) to mock the implementation.

import SalesService from '../SalesService ';test('SalesView shows chart after SalesService returns data', async () => {    const mockSalesService = {        fetchSalesData: jest.fn(() => {            return new Promise((resolve) => {                process.nextTick(() => resolve(weekTestData));            });        })    };    const spy = jest.spyOn(SalesService, 'create').mockImplementation(() => mockSalesService);    const wrapper = await shallow(<SalesView />);    expect(wrapper).toMatchSnapshot();    expect(spy).toHaveBeenCalled();    expect(mockSalesService.fetchSalesData).toHaveBeenCalled();    spy.mockReset();    spy.mockRestore();});


One "ugly" way I've used in the past is to do a sort of poor-man's dependency injection.

It's based on the fact that you might not really want to go about instantiating SalesService every time you need it, but rather you want to hold a single instance per application, which everybody uses. In my case, SalesService required some initial configuration which I didn't want to repeat every time.[1]

So what I did was have a services.ts file which looks like this:

/// In services.tslet salesService: SalesService|null = null;export function setSalesService(s: SalesService) {    salesService = s;}export function getSalesService() {    if(salesService == null) throw new Error('Bad stuff');    return salesService;}

Then, in my application's index.tsx or some similar place I'd have:

/// In index.tsx// initialize stuffconst salesService = new SalesService(/* initialization parameters */)services.setSalesService(salesService);// other initialization, including calls to React.render etc.

In the components you can then just use getSalesService to get a reference to the one SalesService instance per application.

When it comes time to test, you just need to do some setup in your mocha (or whatever) before or beforeEach handlers to call setSalesService with a mock object.

Now, ideally, you'd want to pass in SalesService as a prop to your component, because it is an input to it, and by using getSalesService you're hiding this dependency and possibly causing you grief down the road. But if you need it in a very nested component, or if you're using a router or somesuch, it's becomes quite unwieldy to pass it as a prop.

You might also get away with using something like context, to keep everything inside React as it were.

The "ideal" solution for this would be something like dependency injection, but that's not an option with React AFAIK.


[1] It can also help in providing a single point for serializing remote-service calls, which might be needed at some point.