programing

효소를 사용하여 반응 환원 후크를 사용하여 구성 요소를 테스트하는 방법은 무엇입니까?

bestprogram 2023. 3. 28. 22:44

효소를 사용하여 반응 환원 후크를 사용하여 구성 요소를 테스트하는 방법은 무엇입니까?

효소를 사용하여 테스트하는 리액트 레듀스 훅을 사용하는 간단한 Todo 컴포넌트를 가지고 있습니다만, 아래와 같이 에러가 발생하거나 얕은 렌더로 빈 오브젝트가 표시됩니다.

리액트 리덕스의 후크를 사용하여 구성 요소를 테스트하는 올바른 방법은 무엇입니까?

Todos.js

const Todos = () => {
  const { todos } = useSelector(state => state);

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
};

Todos.test.js v1

...

it('renders without crashing', () => {
  const wrapper = shallow(<Todos />);
  expect(wrapper).toMatchSnapshot();
});

it('should render a ul', () => {
  const wrapper = shallow(<Todos />);
  expect(wrapper.find('ul').length).toBe(1);
});

v1 오류:

...
Invariant Violation: could not find react-redux context value; 
please ensure the component is wrapped in a <Provider>
...

Todos.test.js v2

...
// imported Provider from react-redux 

it('renders without crashing', () => {
  const wrapper = shallow(
    <Provider store={store}>
      <Todos />
    </Provider>,
  );
  expect(wrapper).toMatchSnapshot();
});

it('should render a ul', () => {
  const wrapper = shallow(<Provider store={store}><Todos /></Provider>);
  expect(wrapper.find('ul').length).toBe(1);
});

v2 테스트도 실패합니다.wrapper<Provider>및 호출dive()wrapper는 v1과 동일한 오류를 반환합니다.

사용을 모의하려면선택자 사용을 통해 수행할 수 있습니다.

import * as redux from 'react-redux'

const spy = jest.spyOn(redux, 'useSelector')
spy.mockReturnValue({ username:'test' })

효소 마운트 설비를 사용하여 레독스 훅을 사용하는 컴포넌트를 테스트하여 프로바이더에 모의 스토어를 제공할 수 있습니다.

요소

import React from 'react';
import AppRouter from './Router'
import { useDispatch, useSelector } from 'react-redux'
import StartupActions from './Redux/Startup'
import Startup from './Components/Startup'
import './App.css';

// This is the main component, it includes the router which manages
// routing to different views.
// This is also the right place to declare components which should be
// displayed everywhere, i.e. sockets, services,...
function App () {
  const dispatch = useDispatch()
  const startupComplete = useSelector(state => state.startup.complete)

  if (!startupComplete) {
    setTimeout(() => dispatch(StartupActions.startup()), 1000)
  }

  return (
    <div className="app">
      {startupComplete ? <AppRouter /> : <Startup />}
    </div>
  );
}

export default App;

시험

import React from 'react';
import {Provider} from 'react-redux'
import { mount, shallow } from 'enzyme'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk';
import App from '../App';

const mockStore = configureMockStore([thunk]);

describe('App', () => {
  it('should render a startup component if startup is not complete', () => {
    const store = mockStore({
      startup: { complete: false }
    });
    const wrapper = mount(
      <Provider store={store}>
        <App />
      </Provider>
    )
    expect(wrapper.find('Startup').length).toEqual(1)
  })
})

다른 파일에 정의된 함수 셀렉터를 사용하는 경우 @abidibo 이외의 방법이 있습니다.조롱해도 좋다useSelector셀렉터 기능을 사용하여shallow효소로부터:

요소

import * as React from 'react';
import { useSelector } from 'react-redux';
import Spinner from './Spinner';
import Button from './Button ';
import { getIsSpinnerDisplayed } from './selectors';

const Example = () => {
  const isSpinnerDisplayed = useSelector(getIsSpinnerDisplayed);

  return isSpinnerDisplayed ? <Spinner /> : <Button />;
};

export default Example;

셀렉터

export const getIsSpinnerDisplayed = state => state.isSpinnerDisplayed;

시험

import * as React from 'react';
import { shallow } from 'enzyme';
import Example from './Example';
import Button from './Button ';
import { getIsSpinnerDisplayed } from './selectors';

jest.mock('react-redux', () => ({
  useSelector: jest.fn(fn => fn()),
}));
jest.mock('./selectors');

describe('Example', () => {
  it('should render Button if getIsSpinnerDisplayed returns false', () => {
    getIsSpinnerDisplayed.mockReturnValue(false);

    const wrapper = shallow(<Example />);

    expect(wrapper.find(Button).exists()).toBe(true);
  });
});

조금 진부할 수도 있지만, 저는 잘 동작합니다.

효소의 얕은 렌더링을 사용한 반응 환원 후크 테스트

여기 있는 모든 응답을 읽고 문서를 살펴본 후, 저는 효소가 포함된 리액트 리듀스 후크와 얕은 렌더링을 사용하여 리액트 컴포넌트를 테스트하는 방법을 종합하고 싶었습니다.

이 테스트들은 이 테스트들을 조롱하는 것에 의존합니다.useSelector그리고.useDispatch후크. 제스트와 시논에서도 예를 들어볼게요.

기본적인 농담 예시

import React from 'react';
import { shallow } from 'enzyme';
import * as redux from 'react-redux';
import TodoList from './TodoList';

describe('TodoList', () => {
  let spyOnUseSelector;
  let spyOnUseDispatch;
  let mockDispatch;

  beforeEach(() => {
    // Mock useSelector hook
    spyOnUseSelector = jest.spyOn(redux, 'useSelector');
    spyOnUseSelector.mockReturnValue([{ id: 1, text: 'Old Item' }]);

    // Mock useDispatch hook
    spyOnUseDispatch = jest.spyOn(redux, 'useDispatch');
    // Mock dispatch function returned from useDispatch
    mockDispatch = jest.fn();
    spyOnUseDispatch.mockReturnValue(mockDispatch);
  });

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

  it('should render', () => {
    const wrapper = shallow(<TodoList />);

    expect(wrapper.exists()).toBe(true);
  });

  it('should add a new todo item', () => {
    const wrapper = shallow(<TodoList />);

    // Logic to dispatch 'todoAdded' action

    expect(mockDispatch.mock.calls[0][0]).toEqual({
      type: 'todoAdded',
      payload: 'New Item'
    });
  });
});

기본 Sinon 예시

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import * as redux from 'react-redux';
import TodoList from './TodoList';

describe('TodoList', () => {
  let useSelectorStub;
  let useDispatchStub;
  let dispatchSpy;  

  beforeEach(() => {
    // Mock useSelector hook
    useSelectorStub = sinon.stub(redux, 'useSelector');
    useSelectorStub.returns([{ id: 1, text: 'Old Item' }]);

    // Mock useDispatch hook
    useDispatchStub = sinon.stub(redux, 'useDispatch');
    // Mock dispatch function returned from useDispatch
    dispatchSpy = sinon.spy();
    useDispatchStub.returns(dispatchSpy);
  });

  afterEach(() => {
    sinon.restore();
  });
  
  // More testing logic...
});

여러 use Selector 후크 테스트

복수 테스트useSelectorsRedux 앱 상태를 조롱해야 합니다.

var mockState = {
  todos: [{ id: 1, text: 'Old Item' }]
};

그 후, 델 자신의 실장을 조롱할 수 있습니다.useSelector.

// Jest
const spyOnUseSelector = jest.spyOn(redux, 'useSelector').mockImplementation(cb => cb(mockState));

// Sinon
const useSelectorStub = sinon.stub(redux, 'useSelector').callsFake(cb => cb(mockState));

이게 가장 좋은 동시에 가장 간단한 조롱 방법이라고 생각합니다.useSelector장난삼아 Redux 스토어에서 후크:

  import * as redux from 'react-redux'

  const user = {
    id: 1,
    name: 'User',
  }

  const state = { user }

  jest
    .spyOn(redux, 'useSelector')
    .mockImplementation((callback) => callback(state))

이 아이디어로 인해state스토어 데이터의 서브셋만으로 스토어 모크를 실행할 수 있습니다.

도움말을 검색한 후 useSelector를 시뮬레이션하기 위해 찾은 몇 가지 방법을 조합했습니다.

테스트 전에 먼저 부트스트래핑을 실행하는 함수를 만듭니다.react-redux의 useSelector 함수를 덮어쓰고 모의할 값을 사용하여 저장소를 설정합니다.

스토어 상태가 컴포넌트의 동작에 어떻게 영향을 미치는지 알 수 있는 여러 테스트 케이스를 작성할 때 매우 편리하다고 생각합니다.

import configureMockStore from 'redux-mock-store';
import * as Redux from 'react-redux';
import MyComponent from './MyComponent';

const mockSelectors = (storeValues) => {
  const mockStore = configureMockStore()({
    mobile: {
      isUserConnected: false
      ...storeValues
    },
  });

  jest
    .spyOn(Redux, 'useSelector')
    .mockImplementation(state => state.dependencies[0](mockStore.getState()));
};

describe('isUserConnected: true', () => {
    beforeEach(() => {
      mockSelectors({ isUserConnected: true });
      component = shallow(<MyComponent />);

      test('should render a disconnect button', () => {
         expect(component).toBeDefined();
         expect(component.find('button')).toBeTruthy();
      });
    });
  });

컴포넌트는 다음과 같습니다.

import React from 'react';
import { shallowEqual, useSelector } from 'react-redux';

const MyComponent = () => {    
  const isConnected = useSelector(selectIsConnected, shallowEqual);

  return (
    <>
      {
        showDisconnect ? (
          <button type="button" onClick={() => ()}>disconnect</button>
        ) : null
      }
    </>
  );
};

export default MyComponent;

아래 코드가 좋습니다.

import { configure, shallow} from 'enzyme'; 
import Adapter from 'enzyme-adapter-react-16'; 
import ServiceListingScreen  from './ServiceListingScreen'; 
import renderer from 'react-test-renderer';
import { createStore } from 'redux';
import serviceReducer from './../store/reducers/services'; 
import { Provider } from 'react-redux'
 
const store = createStore(serviceReducer  ) ; 
configure({adapter: new Adapter()}); 


const ReduxProvider = ({ children, reduxStore }) => (
  <Provider store={reduxStore}>{children}</Provider>
)

describe('Screen/ServiceListingScreen', () => {
  it('renders correctly ', () => {
    const wrapper = shallow(<Provider store={store}><ServiceListingScreen  /></Provider>);
    const tree = renderer.create(wrapper).toJSON();
    expect(tree).toMatchSnapshot();
  });
   
});

해 보세요.redux-saga-test-plan은 실행 을 모두 으로 cool redox assertion을 실행합니다.

const mockState = { rick:"Genius", morty:"dumbAsss"}

expectSaga(yourCoolSaga)
      .provide({
        select({ selector }, next) {
          if (selector) {
            return mockState;
          }
          return next();
        }
      })
    // some assertion here
      .put(actions.updateRickMortyPowers(mockState))
      .silentRun();

위의 답변 중 일부는 useSelector 또는 useDispatch를 조롱했지만 redux 문서에서는 실제로 이에 대해 조언하고 있습니다.이 답변은 이쪽에서 확인하세요.https://stackoverflow.com/a/74024854/14432913은 저를 위해 작동했고 redx documents의 예를 따릅니다.

저도 마찬가지입니다.

import { shallow, mount } from "enzyme";
const store = mockStore({
  startup: { complete: false }
});

describe("==== Testing App ======", () => {
  const setUpFn = props => {
    return mount(
      <Provider store={store}>
        <App />
      </Provider>
    );
  };

  let wrapper;
  beforeEach(() => {
    wrapper = setUpFn();
  });

언급URL : https://stackoverflow.com/questions/56827300/how-to-test-a-component-using-react-redux-hooks-with-enzyme