首页 > 解决方案 > 由于选择器未定义,Jest 单元测试失败

问题描述

我正在为一个组件编写一个测试用例,它正在使用 redux、saga 和选择器测试用例如下

const initialState = {
  fetching: false,
  error: null,
  StockProducts: [],
  products: [],
  filter: '5b438ae60599132cb8b64b67'
}

const mockStore = configureStore()
const store = mockStore(fromJS(initialState))

import Stock from '../Stock'

test('for onUpdate funcion', () => {
  const props = {
    setErrorPopUp: jest.fn(),
    formSubmitAttempt: jest.fn()
  }
  const wrapper = shallow(<Stock {...props} store={store} />)
  wrapper.instance().onUpdate({ quantity: 0 })
  expect(props.setErrorPopUp).toHaveBeenCalled()

  wrapper.instance().onUpdate({ quantity: 3 })
  expect(props.formSubmitAttempt).toHaveBeenCalled()
})

但是测试失败了,我收到了这个错误

TypeError: Cannot read property 'fetching' of undefined

      12 |   createSelector(
      13 |     selectStockDomain,
    > 14 |     ({ fetching }) => fetching
      15 |   )
      16 |
      17 | const StockError = () =>

那是来自我用于组件的选择器之一 我的测试出了什么问题,为什么会发生这种情况以及我该如何解决这个问题

这是库存组件

    // react core
import React, { Component } from 'react'

import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'

// component imports

// Selcetors and actions
import StockActions from './actions'
import ToastActions from 'containers/Toasts/actions'
import {
  StockFetching,
  StockError,
  StockProducts,
  StockFilter,
  Products,
  userData
} from './selectors'

// partials

const mapStateToProps = createStructuredSelector({
  fetching: StockFetching(),
  error: StockError(),
  stockProducts: StockProducts(),
  products: Products(),
  filter: StockFilter(),
  userData: userData()
})

const mapDispatchToProps = dispatch => ({
  getStockProducts: () => {
    dispatch(StockActions.getStockAttempt())
  },
  getProducts: payload => {
    dispatch(StockActions.getProductsAttempt(payload))
  },
  formSubmitAttempt: payload => {
    dispatch(StockActions.formSubmitAttempt(payload))
  },
  deleteStock: payload => {
    dispatch(StockActions.deleteStockAttempt(payload))
  },
  setErrorPopUp: payload => {
    dispatch(
      ToastActions.setToast(payload.message, payload.action, payload.time)
    )
  },
  reset: () => {
    dispatch(StockActions.reset())
  }
})

class Stock extends Component {
  state = {
    openCard: false,
    showCard: false,
    confirmDelete: false
  }

  // to show product details
  onCard = openCard => {
    this.setState({
      openCard
    })
  }

  // to show and close add to stock option
  _ShowCard = () => {
    this.setState({
      showCard: !this.state.showCard
    })
  }

  // onDlete Stock
  onDeleteStock = data => {
    let {
      props: { deleteStock },
      state: { confirmDelete }
    } = this
    if (confirmDelete) {
      deleteStock(data)
      this.setState({
        confirmDelete: false
      })
    } else {
      this.setState({
        confirmDelete: true
      })
    }
  }

  // update the products
  handlesubmit = data => {
    if (parseInt(data.quantity) < 1) {
      this.props.setErrorPopUp({
        message:
          'You cannot set quantity to zero.Please enter valid qunatity and try again',
        action: 'danger',
        time: '5000'
      })
    } else {
      this.props.formSubmitAttempt({
        product: data.product.key,
        quantity: data.quantity
      })
      this._ShowCard()
    }
  }

  onUpdate = data => {
    if (parseInt(data.quantity) < 1) {
      this.props.setErrorPopUp({
        message: 'You cannot set quantity to zero.Use delete actions',
        action: 'danger',
        time: '5000'
      })
    } else {
      this.props.formSubmitAttempt({
        product: data.id,
        quantity: data.quantity
      })
    }
  }

  onCancelDelete = () => {
    this.setState({
      confirmDelete: false
    })
  }

  onGetStock = () => {
    let { getStockProducts, getProducts, userData } = this.props
    getStockProducts()
    getProducts({ user: userData._id })
  }

  componentDidMount () {
    this.onGetStock()
  }

  render () {
    let {
      props: { stockProducts, error, products, fetching },
      state: { openCard, showCard, confirmDelete },
      onCard,
      onGetStock,
      _ShowCard,
      handlesubmit,
      onUpdate,
      onDeleteStock,
      onCancelDelete
    } = this
    return (
      <KeyboardAvoidingWrapper fluid enabled={!showCard}>
            UI GOES HERE ....
      </KeyboardAvoidingWrapper>
    )
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Stock)

谢谢

标签: reactjsjestjs

解决方案


当您测试一个组件时,最好只测试组件的行为,而与您的 redux 存储无关。因为,在内部,商店将状态作为道具传递给组件。

1.export你的组件

所以你的组件定义代码变成

class Stock extends Component {

export class Stock extends Component {

2.import只是没有 Connect HOC 的组件

像这样

import { Stock } from '../Stock'

test('for onUpdate funcion', () => {
  const props = {
    setErrorPopUp: jest.fn(),
    formSubmitAttempt: jest.fn()
  }
  const wrapper = shallow(<Stock {...props} />)

  // modify your code to see how your component behaves with different props
  wrapper.instance().onUpdate({ quantity: 0 })
  expect(props.setErrorPopUp).toHaveBeenCalled()

  wrapper.instance().onUpdate({ quantity: 3 })
  expect(props.formSubmitAttempt).toHaveBeenCalled()
})

推荐阅读