I have the following (simplified) React component.
class SalesView extends Component<{}, State> { state: State = { salesData: null }; componentDidMount() { this.fetchSalesData(); } render() { if (this.state.salesData) { return <SalesChart salesData={this.state.salesData} />; } else { return <p>Loading</p>; } } async fetchSalesData() { let data = await new SalesService().fetchSalesData(); this.setState({ salesData: data }); } }
When mounting, I fetch data from an API, which I have abstracted away in a class called SalesService
. This class I want to mock, and for the method fetchSalesData
I want to specify the return data (in a promise).
This is more or less how I want my test case to look like:
setup mockSalesService to return a promise that returns the predefined test data when resolved
create the component
Testing the looks of SalesChart is not part of this question, I hope to solve that using Enzyme. I have been trying dozens of things to mock this asynchronous call, but I cannot seem to get this mocked properly. I have found the following examples of Jest mocking online, but they do not seem to cover this basic usage.
My questions are:
One example that I have that does not work is given below. The test runner crashes with the error throw err;
and the last line in the stack trace is at process._tickCallback (internal/process/next_tick.js:188:7)
# __tests__/SalesView-test.js import React from 'react'; import SalesView from '../SalesView'; jest.mock('../SalesService'); const salesServiceMock = require('../SalesService').default; const weekTestData = []; test('SalesView shows chart after SalesService returns data', async () => { salesServiceMock.fetchSalesData.mockImplementation(() => { console.log('Mock is called'); return new Promise((resolve) => { process.nextTick(() => resolve(weekTestData)); }); }); const wrapper = await shallow(<SalesView/>); expect(wrapper).toMatchSnapshot(); });
The asynchronous function can be written in Node. js using 'async' preceding the function name. The asynchronous function returns implicit Promise as a result. The async function helps to write promise-based code asynchronously via the event-loop.
In the File menu, click New Project. In the "New Project" dialog box, under Project types, expand Visual C#, and then click "Web". In the Name box, type "Async", then click on Ok. Now, in the dialog box, click on "MVC" under the ASP.NET 4.5.
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(); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With