web-dev-qa-db-fra.com

Comment puis-je tester A React Crows Composant en changeant de l'utilisateur

J'ai un React Crows Composant fonctionnel que j'aimerais tester avec Jest/Enzyme. J'aimerais tester son comportement de rendu tertiaire en fonction d'une valeur de l'UESTATE. Je n'arrive pas à trouver exemple en ligne. Il n'y a pas de "clic" pour simuler - aucun appel de l'API à maquette, car à la fin, je dois toujours tester en fonction de la valeur de l'USESTE. Dans le passé, avec des composants de classe, je pourrais régler l'état. Avec le nouveau Crochets, je ne peux pas. Donc, fondamentalement, comment est-ce que je me moque d'une asynchreur d'une asynchreur à l'intérieur d'une fonction d'émetteur moquée de sorte que le rendu se comporte correctement?

Voici mon composant:

import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';

import Form from 'core/Form';

export const Parent = ({submitForm}) => {
  const [formValues, setFormValues] = useState({});
  const [redirect, setRedirect] = useState(false);

  const handleChange = name => evt => {
    setFormValues({ ...formValues, [name]: evt.target.value });
  };

  const onSubmit = async () => {
      try {
        const res = await submitForm(formValues);
        if (res) setRedirect(true);
        else setRedirect(false);
      } catch (err) {
        console.log('Submit error: ', err);
      }
  };

  return redirect ? (
    <Redirect Push to={path} />
  ) : (
    <Form onSubmit={onSubmit} values={formValues} onChange={handleChange} />
  );
};

export default Parent;

Voici mes tests jusqu'à présent:

import React from 'react';
import { shallow } from 'enzyme';
import { Redirect } from 'react-router-dom';

import Parent from './Parent';
import Form from 'core/Form';

let wrapper, props;
.
.
.

describe('<Parent /> rendering', () => {
  beforeEach(() => {
    props = createTestProps();
    wrapper = shallow(<Parent {...props} />);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  const setState = jest.fn();
  const useStateSpy = jest.spyOn(React, 'useState');
  useStateSpy.mockImplementation(init => [init, setState]);

  it('Should render 1 Form', () => {
    expect(wrapper.find(Form)).toHaveLength(1);
  });

  it('renders Redirect after API call', () => {
    setRedirect = jest.fn(() => false);

    expect(wrapper.find(Redirect)).toHaveLength(1);
  });

  it('renders Form before API call', () => {
    setRedirect = jest.fn(() => true);

    expect(wrapper.find(Form)).toHaveLength(1);
  });
});
2
Adrian Bartholomew

Vous n'avez pas besoin d'espionner useState crochet. Ce qui signifie que vous ne devez pas tester directement ces crochets et ces méthodes du composant. Au lieu de cela, vous devez tester le comportement des composants (le state, props et ce qui est rendu)

Par exemple.

index.tsx:

import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';

export const Form = ({ onSubmit, onChange, values }) => <form onSubmit={onSubmit}></form>;
const path = '/user';

export const Parent = ({ submitForm }) => {
  const [formValues, setFormValues] = useState({});
  const [redirect, setRedirect] = useState(false);

  const handleChange = (name) => (evt) => {
    setFormValues({ ...formValues, [name]: evt.target.value });
  };

  const onSubmit = async () => {
    try {
      const res = await submitForm(formValues);
      if (res) setRedirect(true);
      else setRedirect(false);
    } catch (err) {
      console.log('Submit error: ', err);
    }
  };

  return redirect ? (
    <Redirect Push to={path} />
  ) : (
    <Form onSubmit={onSubmit} values={formValues} onChange={handleChange} />
  );
};

export default Parent;

index.test.tsx:

import Parent, { Form } from './';
import React from 'react';
import { shallow } from 'enzyme';
import { Redirect } from 'react-router-dom';
import { act } from 'react-dom/test-utils';

const whenStable = async () =>
  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });

describe('60137762', () => {
  it('should render Form', () => {
    const props = { submitForm: jest.fn() };
    const wrapper = shallow(<Parent {...props}></Parent>);
    expect(wrapper.find(Form)).toBeTruthy();
  });

  it('should handle submit and render Redirect', async () => {
    const props = { submitForm: jest.fn().mockResolvedValueOnce(true) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(wrapper.find(Redirect)).toBeTruthy();
  });

  it('should handle submit and render Form', async () => {
    const props = { submitForm: jest.fn().mockResolvedValueOnce(false) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(wrapper.find(Form)).toBeTruthy();
  });

  it('should handle error if submit failure', async () => {
    const logSpy = jest.spyOn(console, 'log');
    const mError = new Error('network');
    const props = { submitForm: jest.fn().mockRejectedValueOnce(mError) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(logSpy).toHaveBeenCalledWith('Submit error: ', mError);
  });
});

Résultats du test unitaire avec rapport de couverture:

 PASS  stackoverflow/60137762/index.test.tsx
  60137762
    ✓ should render Form (18ms)
    ✓ should handle submit and render Redirect (15ms)
    ✓ should handle submit and render Form (8ms)
    ✓ should handle error if submit failure (18ms)

  console.log node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866
    Submit error:  Error: network
        at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:39:20
        at step (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:44:23)
        at Object.next (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:25:53)
        at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:19:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:15:12)
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:37:47)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:73:41)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   78.57 |      100 |      40 |   93.75 |                   
 index.tsx |   78.57 |      100 |      40 |   93.75 | 12                
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        3.716s, estimated 5s

Code source: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60137762

2
slideshowp2