Home > Net >  Service method mock with Jest in React app
Service method mock with Jest in React app

Time:09-23

I am using React with Typescript and Jest for unit testing. I'm not using Enzyme.

I'm trying to understand the mocking capabilities with Jest and specifically to determine something wrong.

I've created a react app using the create-react-app with typescript, i.e.

npx create-react-app my-app --template typescript

My next step was to create the following inside the src directory.

// services/serviceResponse.ts

export interface ServiceResponse {
    label: string;
    id: string;
  }

// services/myservice.ts

import { ServiceResponse } from "./serviceResponse";

const cannedServiceResponse: ServiceResponse[] = [
    {
        label: 'label1',
        id: 'id1',
    },
    {
        
        label: 'label2',
        id: 'id2',
    },
]

async function GetServiceData(): Promise<ServiceResponse[] | Error> {
    return cannedServiceResponse;
}
  
export default {
    GetServiceData,
};
  
// components/mycomponent.tsx
import React, { useState, useEffect } from 'react';
import myservice from '../services/myservice';
import { Row } from 'react-bootstrap';
import { ServiceResponse } from '../services/serviceResponse';

export const MyComponent = () => {
    const [data, setData] = useState<ServiceResponse[] | undefined>();

    useEffect(() => {
        const fetchData = async () => {
        const serviceData = await myservice.GetServiceData();
        if (!(serviceData instanceof Error)) {
            setData(serviceData);
        }
        };

        fetchData();
    }, []);

    if (!data) {
        return <></>;
    }

    return (
        <>
            <Row xs={1} md={4} className="col-lg-12">
            </Row>
        </>
    );
};

Then I want to unit test the Component by mocking the data returned from the service method GetServiceData. My code for the test component is

// components/mycomponents.test.tsx
import React from 'react';
import TestRenderer from 'react-test-renderer';
import { MyComponent } from './mycomponent';
import myservice from '../services/myservice';
import { Row } from 'react-bootstrap';
import { ServiceResponse } from '../services/serviceResponse';

const mockData: ServiceResponse[] = [
  { label: 'a', id: 'b'},
  { label: 'f', id: 'g'},
];

describe('mycomponent', () => {
  
  it('MyComponent returns data', () => {
    jest.spyOn(myservice, 'GetServiceData').mockImplementation(
      () =>
        new Promise<ServiceResponse[]>((resolve) => {
          resolve(mockData);
        })
    );

    const component = TestRenderer.create(<MyComponent />);
    const componentInstance = component.root;
    console.log(component.toJSON());
    // TODO - Add an expect line here for assertion
  });
});

The problem is that the jest.spyOn() doesn't mock the data. I tried using the following also

jest.mock('../services/myservice', () => ({
  GetServiceData: jest.fn().mockReturnValue(() => Promise.resolve(mockData)),
}));

I understand there are ways to test with Enzyme as used here but I'm trying to understand mocking with Jest and what I'm doing wrong.

CodePudding user response:

jest.spyOn() should work. You need to use the asynchronous version of act to apply resolved promises. You can refer to the official example testing-recipes.html#data-fetching

import React from 'react';
import { act, create } from 'react-test-renderer';
import { MyComponent } from './mycomponent';
import myservice from '../services/myservice';
import { ServiceResponse } from '../services/serviceResponse';

const mockData: ServiceResponse[] = [
  { label: 'a', id: 'b' },
  { label: 'f', id: 'g' },
];

describe('mycomponent', () => {
  it('MyComponent returns data', async () => {
    jest.spyOn(myservice, 'GetServiceData').mockResolvedValue(mockData);
    let root;
    await act(async () => {
      root = create(<MyComponent />);
    });
    console.log(root.toJSON());
  });
});

test result:

 PASS  examples/69293771/components/mycomponent.test.tsx (7.271 s)
  mycomponent
    ✓ MyComponent returns data (25 ms)

  console.log
    { type: 'div', props: {}, children: [ 'row' ] }

      at examples/69293771/components/mycomponent.test.tsx:19:13

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        7.755 s, estimated 9 s
  • Related