zoukankan      html  css  js  c++  java
  • [React Testing] Create a Custom Render Function to Simplify Tests of Redux Components

    It’s very common for our components to use state from the redux store (or to be refactored to use it eventually). Having to change our tests every time we do this would be frustrating. Let’s see how we can write a render method we could use to not worry about that implementation detail when it’s irrelevant for our tests. This could be combined with the custom render method for the router to be a single render method that supports all providers our components need.

    Component:

    import React from 'react'
    import { useSelector } from 'react-redux'
    import { rootSelector } from '../reducers/beerReducer'
    import BeerList from './BeerList'
    
    export default function Beers() {
      const { data, status, messages } = useSelector(rootSelector)
    
      return (
        <>
          {status === 'idle' && (
            <div className="App-content">
              <p>Get started by searching beers or get random ones</p>
            </div>
          )}
          {status === 'pending' && (
            <div className="App-content">
              <p>Loading Beers!...</p>
            </div>
          )}
          {status === 'success' && (
            <div className="App-content">
              <BeerList beers={data} />
            </div>
          )}
          {status === 'failure' && (
            <div className="App-messages">
              <p>Oops! {messages[0].text}</p>
            </div>
          )}
        </>
      )
    }

    Reducer:

    import {
      SET_STATUS,
      FETCH_FULFILLED,
      FETCH_FAILED,
      RESET,
    } from '../actions/beerActions'
    import { createReducer } from '../utils/utils'
    import { lensProp, set, view, compose, prop } from 'ramda'
    
    const initialState = {
      data: [],
      status: 'idle', // 'idle' || 'pending' || 'success' || 'failure'
      messages: [],
    }
    
    const rootLens = lensProp('beers')
    const statusLens = lensProp('status')
    const dataLens = lensProp('data')
    const messagesLens = lensProp('messages')
    
    const setStatus = set(statusLens)
    const setData = set(dataLens)
    const setMessages = set(messagesLens)
    
    const viewRoot = (lens) => view(compose(rootLens, lens))
    const viewStatus = viewRoot(statusLens)
    const viewData = viewRoot(dataLens)
    const viewMessages = viewRoot(messagesLens)
    
    const fetchFulfilled = (data, state) => {
      return compose(setData(data), setStatus('success'), setMessages([]))(state)
    }
    
    const reset = (_, state) => compose(setStatus('idle'), setMessages([]))(state)
    
    const fetchFailed = (messages, state) => {
      return compose(
        setStatus('failure'),
        setMessages([{ type: 'error', text: messages }]),
      )(state)
    }
    
    const reducers = {
      [RESET]: reset,
      [SET_STATUS]: setStatus,
      [FETCH_FULFILLED]: fetchFulfilled,
      [FETCH_FAILED]: fetchFailed,
    }
    
    export const beersReducers = (state = initialState, action) =>
      createReducer(reducers, state, action)
    
    export const rootSelector = prop('beers')
    export const beersSelector = viewData
    export const messagesSelector = viewMessages
    export const statusSelector = viewStatus
    export const createReducer = (reducers, state, { type, payload }) =>
      propOr(() => state, type, reducers)(payload, state)

    Test:

    import React, { Children } from 'react'
    import { render, queryByTestId, queryAllByTestId } from '@testing-library/react'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    
    import Beers from '../Beers'
    import { beersReducers } from '../../reducers/beerReducer'
    import { configureStore } from '../../configureStore'
    
    function renderReducer(ui, options = {}) {
      const store =
        options.store || createStore(options.reducer, options.initialState)
      function Wrapper({ children }) {
        return <Provider store={store}>{children}</Provider>
      }
      return {
        ...render(ui, { wrapper: Wrapper, ...options }),
        store,
      }
    }
    
    test('can render redux with custom initial state', () => {
      const { getByText } = renderReducer(<Beers />, {
        reducer: beersReducers,
        initialState: {
          beers: {
            status: 'pending',
            data: [],
            messages: [],
          },
        },
      })
    
      expect(getByText(/loading Beers!.../i)).toBeTruthy()
    })
    
    test('can render redux with custom initial state', () => {
      const { queryByTestId, queryAllByTestId } = renderReducer(<Beers />, {
        reducer: beersReducers,
        initialState: {
          beers: {
            status: 'success',
            data: [
              {
                id: 1,
                name: 'some beer',
                volume: { unit: 1 },
                abv: '',
              },
            ],
            messages: [],
          },
        },
      })
    
      expect(queryByTestId('beerList')).toBeTruthy()
      expect(queryAllByTestId('beerItem').length).toEqual(1)
    })
    
    test('can render initial status with a hint for search', () => {
      const { getByText, debug } = renderReducer(<Beers />, {
        store: configureStore(),
      })
      expect(getByText(/get started by searching beers/i)).toBeTruthy()
    })
  • 相关阅读:
    vim介绍 & vim颜色显示和移动光标& vim一般模式下移动光标 & vim一般模式下复制、剪切和粘贴
    lvm 详解 磁盘故障小案例
    磁盘格式化、磁盘挂载、手动增加swap空间
    df du 磁盘分区
    su sudo 限制root远程登录
    usermod 用户密码管理 mkpasswd
    顺序查找,二分法查找,插值查找算法实现及分析
    完全二叉树的构建及三种遍历
    Mybatis由于类型转换出现的问题
    delimiter解释
  • 原文地址:https://www.cnblogs.com/Answer1215/p/12824929.html
Copyright © 2011-2022 走看看