【问题标题】:Testing React Async with Jest and create-react-app使用 Jest 和 create-react-app 测试 React Async
【发布时间】:2017-07-04 17:31:55
【问题描述】:

我似乎无法弄清楚这一点。我正在使用 create-react-app,它内置在测试运行程序 Jest 中。对于所有同步代码,它似乎工作得很好,但是在模拟承诺时,我似乎无法让它工作。

一个反应组件有一个我可以模拟提交的表单。

React 组件代码 sn-ps。

//Top of the page
import {auth} from '../../lib/API_V2'
// ... //

// Handle submit runs when the form is submitted
handleSubmit = (event) => {
  console.log('submit')
  event.preventDefault()
  this.setState(prevState => ({
    ...prevState,
    loading: true
  }))
  console.log('stateSet')
  auth(this.state.userName, this.state.password)
    .then(results => {
      // NEVER RUNS
      console.log('then')
      // stuff omitted
      this.setState(prevState => ({
        ...prevState,
        loading: false
      }))
      this.props.afterAuth()
    })
  .catch(() => {
    // also never runs
    // omitted
    this.setState(prevState => ({
      ...prevState,
      loading: false
    }))
    this.props.afterAuth()
  })
}

测试代码

jest.mock('../../lib/API_V2')
it.only(`should mock a login`, () => {
  const myMock = jest.fn()
  const authComp = mount(<AuthComponent afterAuth={myMock}/>)

  authComp.find('.userName').simulate('change', {target: {value: 'userName'}})
  authComp.find('.password').simulate('change', {target: {value: 'password'}})
  expect(authComp.state().userName).toEqual('userName')
  expect(authComp.state().password).toEqual('password')
  authComp.find('[type="submit"]').get(0).click()
  expect(myMock.mock.calls.length).toBe(1) // FAILS
})

API 库返回一个承诺。我没有使用它,而是在它旁边有一个__mocks__/API_V2.js。看起来像这样

function auth (lastname, accountNumber) {
  console.log('yay!?')
  return new Promise((resolve) => {
    resolve({
      accountNumber,
      lastName: lastname
    })
  })
}     

我的模拟测试代码似乎从未运行过。如果我记录模拟函数,我会得到function auth() {return mockConstructor.apply(this,arguments);}

我已尝试按照说明进行操作 https://facebook.github.io/jest/docs/tutorial-async.html,但似乎没有调用我的模拟方法。实际的方法也不是。相反,我对auth() 的调用返回未定义。

有人有什么想法吗?

-- 补充信息--

src
  Components
    AuthComponent
      AuthComponent.js
      AuthComponent.test.js
      index.js
  Lib
    API_V2
      API_V2.js
      index.js
      __mocks__
        API_V2.js

【问题讨论】:

  • 我最终手动模拟了它,而不是使用 mocks 目录。 jest.mock('../../lib/API_V2, () => {auth: function ...})

标签: javascript reactjs jestjs create-react-app


【解决方案1】:

我认为您遇到了与此问题相关的错误:https://github.com/facebook/jest/issues/2070

由于您实际上是在尝试导入名为API_V2/index.js 的文件,因此您需要模拟index.js。但是,这样做会很糟糕,因为它将成为您尝试模拟的每个 index.js 文件的有效模拟。

目前最好的方法是重写一些代码以使用依赖注入并将模拟传递给任何需要使用{ auth }

【讨论】:

  • 根据我的经验,它只是让我恼火的警告,但同名的模拟并没有被“交换”。
  • > 但是,这样做会很糟糕,因为它将成为您尝试模拟的每个 index.js 文件的有效模拟。我不认为这是正确的。如果他把它放在Lib/API_V2/__mocks__/index.js 中应该可以工作并且只模拟Lib/API_V2/index.js
  • 抱歉,刚刚查看了引用的错误。这很烦人。也许只是重命名文件? ;-)
【解决方案2】:

在模拟的新 Promise 中,即使您立即解决,此解决也不会同步发生。 Promise 回调始终作为排队的 microtask 运行,因此当您在测试中模拟点击时,您的模拟中的 Promise 回调尚未运行(因此 myMock 也尚未被调用) .这就是您的期望失败的原因。

解决此问题的一种(有点老套)方法是使用 setTimeout。 setTimeout 会将一个任务排入队列,并且任务总是在微任务之后运行。 Jest 通过从 it 回调返回 Promises 来支持异步测试,所以你可以这样写:

jest.mock('../../lib/API_V2')
it.only(`should mock a login`, () => new Promise(resolve => {
  const myMock = jest.fn()
  const authComp = mount(<AuthComponent afterAuth={myMock}/>)

  authComp.find('.userName').simulate('change', {target: {value: 'userName'}})
  authComp.find('.password').simulate('change', {target: {value: 'password'}})
  expect(authComp.state().userName).toEqual('userName')
  expect(authComp.state().password).toEqual('password')
  authComp.find('[type="submit"]').get(0).click()
  setTimeout(() => {
    expect(myMock.mock.calls.length).toBe(1)
    resolve() // Tell jest this test is done running
  }, 0);
}))

这里很好地解释了任务和微任务的工作原理:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

【讨论】:

    猜你喜欢
    • 2019-01-30
    • 2020-07-22
    • 2020-08-09
    • 2020-01-11
    • 2017-02-09
    • 2018-03-17
    • 2019-08-23
    • 2020-09-16
    • 2017-02-16
    相关资源
    最近更新 更多