2

I want to test if my state is updated after an API call in my component. I have a method lets say method1() and in that method, it calls fetch and sets the state to the results. Here is the method:

method1 = () => {
if (this.state.state1 !== "") {
  fetch('api')
  .then((resp) => resp.json())
  .then((data) => {
    this.setState({ state2: data });
  })
  .catch((error) => {
    console.log(error);
  });
}

}

In my test file I already mocked the API with fetch-mock and here is the test trying to test this:

it('updates the state after the api call', () => {
  const instance = component.instance();
  instance.method1();
  expect(component.state('state2')).toBe(MOCK_DATA);
});

My MOCK_DATA is defined along with the mock fetches and component (shallow) in beforeEach()

When I run this, state2 is still in its initial state ([]), but I'm expecting it to be a populated array.

I also tried making the test async and using await but I am getting the same results.

Any help would be appreciated! Thanks!

1 Answer 1

0

Here is a solution without using fetch-mock. You can mock fetch manually by yourself.

index.tsx:

import React, { Component } from 'react';
import fetch from 'node-fetch';

interface ISomeComponentState {
  state1: string;
  state2: string;
}

export class SomeComponent extends Component<any, ISomeComponentState> {
  constructor(props) {
    super(props);
    this.state = {
      state1: 'jest',
      state2: ''
    };
  }

  public async method1() {
    if (this.state.state1 !== '') {
      await fetch('api')
        .then(resp => resp.json())
        .then(data => {
          this.setState({ state2: data });
        })
        .catch(error => {
          console.log(error);
        });
    }
  }

  public render() {
    return <div>test</div>;
  }
}

Unit test, index.spec.tsx:

import React from 'react';
import { SomeComponent } from './';
import { shallow, ShallowWrapper } from 'enzyme';
import fetch from 'node-fetch';

const { Response } = jest.requireActual('node-fetch');

jest.mock('node-fetch');

describe('SomeComponent', () => {
  let component: ShallowWrapper;
  beforeEach(() => {
    component = shallow(<SomeComponent></SomeComponent>);
  });

  it('snapshot', () => {
    expect(component).toMatchSnapshot();
  });

  it('should not fetch api', async () => {
    const mockedState = { state1: '', state2: '' };
    component.setState(mockedState);
    expect(component.state()).toEqual(mockedState);
    await (component.instance() as SomeComponent).method1();
    expect(fetch).not.toBeCalled();
    expect(component.state()).toEqual(mockedState);
  });

  it('should fetch api correctly', async () => {
    (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(new Response(JSON.stringify('mocked data')));
    expect(component.state()).toEqual({ state1: 'jest', state2: '' });
    await (component.instance() as SomeComponent).method1();
    expect(fetch).toBeCalledWith('api');
    expect(component.state()).toEqual({ state1: 'jest', state2: 'mocked data' });
  });

  it('will fetch error', async () => {
    const mockedError = new Error('some error');
    const consoleLogSpyOn = jest.spyOn(console, 'log');
    (fetch as jest.MockedFunction<typeof fetch>).mockRejectedValueOnce(mockedError);
    await (component.instance() as SomeComponent).method1();
    expect(fetch).toBeCalledWith('api');
    expect(consoleLogSpyOn).toBeCalledWith(mockedError);
    expect(component.state()).toEqual({ state1: 'jest', state2: '' });
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/52899150/index.spec.tsx
  SomeComponent
    ✓ snapshot (20ms)
    ✓ should not fetch api (5ms)
    ✓ should fetch api correctly (6ms)
    ✓ will fetch error (11ms)

  console.log node_modules/jest-mock/build/index.js:860
    Error: some error
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:38:25)
        at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:32:23)
        at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:13:53)
        at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:7:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:3:12)
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/52899150/index.spec.tsx:37:26)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
        at process._tickCallback (internal/process/next_tick.js:68:7)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |      100 |      100 |      100 |      100 |                   |
 index.tsx |      100 |      100 |      100 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   1 passed, 1 total
Time:        4.697s

Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/52899150

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.