简体   繁体   English

使用Jest和create-react-app测试React Async

[英]Testing React Async with Jest and create-react-app

I can't seem to figure this one out. 我似乎无法想出这个。 I'm using create-react-app and it's built in test runner Jest. 我正在使用create-react-app,它是在测试运行器Jest中构建的。 For all synchronous code it seems to work really well, but when mocking promises I can't seem to get it to work. 对于所有同步代码,它似乎工作得很好,但是当嘲笑承诺时,我似乎无法让它工作。

A react component has a form that I'm able to simulate a submit. 反应组件具有我能够模拟提交的形式。

React component code snippets. 反应组件代码段。

//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()
  })
}

Test code 测试代码

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
})

The API lib returns a promise. API lib返回一个promise。 Instead of using that I have a __mocks__/API_V2.js next to it. 而不是使用它,我旁边有一个__mocks__/API_V2.js That looks like this 看起来像这样

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

My mock test code never seems to be run. 我的模拟测试代码似乎永远不会运行。 If I log the mock function I get function auth() {return mockConstructor.apply(this,arguments);} 如果我记录模拟函数,我得到function auth() {return mockConstructor.apply(this,arguments);}

I've tried to follow the instructions https://facebook.github.io/jest/docs/tutorial-async.html but it seems as though my mock methods aren't being called. 我试图按照说明https://facebook.github.io/jest/docs/tutorial-async.html但似乎我的模拟方法没有被调用。 And neither are the actual methods. 实际方法也不是。 Instead my call to auth() returns undefined. 相反,我对auth()调用返回undefined。

Anyone have any ideas? 有人有主意吗?

-- Supplementary Information -- - 补充资料 -

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

I think you're hitting a bug related to this issue: https://github.com/facebook/jest/issues/2070 我认为你遇到了与此问题相关的错误: https//github.com/facebook/jest/issues/2070

Since you're actually trying to import a file called API_V2/index.js , you need to mock index.js . 由于您实际上是在尝试导入名为API_V2/index.js的文件,因此需要模拟index.js However, you're going to have a really bad time doing that, since it'll be a valid mock for every index.js file that you try to mock. 但是,这样做会非常糟糕,因为它对于您尝试模拟的每个 index.js文件都是有效的模拟。

The best way to do this at the moment is to rewrite some of your code to use dependency-injection and pass in a mock to whatever needs to use { auth } 目前执行此操作的最佳方法是重写一些代码以使用依赖注入并将模拟传递给需要使用的任何内容{ auth }

In the new Promise from your mock, even though you immediately resolve, this resolution does not occur synchronously. 在您的模拟的新Promise中,即使您立即解决,此解决方案也不会同步发生。 Promise callbacks always run as an enqueued microtask , so when you simulate a click in your test, the Promise callback in your mock has not yet run (and so myMock has not been called yet, either). Promise回调总是作为一个排队的微任务来运行,所以当你在测试中模拟一个点击时,你的模拟中的Promise回调还没有运行(所以myMock还没有被调用)。 This is why your expectation fails. 这就是你的期望失败的原因。

One (somewhat hacky) way you could work around this issue would be with a setTimeout. 你可以解决这个问题的一种(有点hacky)方法是使用setTimeout。 setTimeout will enqueue a task , and tasks always run after microtasks. setTimeout将排队任务 ,任务总是在微任务之后运行。 Jest supports async tests via returning Promises from it callbacks, so you might write: 开玩笑通过返回的承诺,从支持异步测试it回调,所以你可能会这样写:

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);
}))

There is a good explanation of how tasks and microtasks work here: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 有关任务和微任务如何在这里工作的一个很好的解释: https ://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM