首页 > 解决方案 > Jest mock 并不总是适用于异步测试

问题描述

我有一个函数,我想用 Jest 测试它。

function handleRegister() {
    return (req, res) => {
  
      try {
        const credentials = {
          login: req.body.email,
          password: req.body.password
        }
  
        res.status(201).send({ msg: 'User registration achieved successfully' })  //LINE 10
        res.status(201).send({ msg: 'User registration achieved successfully' })  //LINE 11
  
        auth.register(credentials, (err, result) => {
          console.log('register', auth.getUsers())
  
          if (result.status === 201) {
            res.status(201).send({ msg: 'User registration achieved successfully' })  //LINE 17
            console.log('User registration achieved successfully')
          }
        })
    } catch(err) {

    }
}}

我的测试代码是:

test('should return status 201 and msg', done => {

  try {
    const fun = handlers.handleRegister()

    const res = {
      status: jest.fn().mockReturnThis(),
      send: function () {
        done()
      }
    }


    fun({ body: { email: 'a', password: 'a' } }, res)

    expect(res.status).toBeCalledWith(201)

  } catch(err) {
    done(err)
  }
})

问题是函数 handlerRegister 第 10 行和第 11 行已正确执行,但在第 17 行出现错误:

/home/anna/Desktop/dev/exampleShop/backend/handlers.js:149
          res.status(201).send({
                       ^
TypeError: Cannot read property 'send' of undefined
    at auth.register (/home/anna/Desktop/dev/exampleShop/backend/handlers.js:149:26)
    at addAccountToDB (/home/anna/Desktop/dev/exampleShop/backend/auth.js:69:7)
    at addAccountToDB (/home/anna/Desktop/dev/exampleShop/backend/auth.js:81:3)
    at hashPassword (/home/anna/Desktop/dev/exampleShop/backend/auth.js:68:5)
    at AsyncWrap.crypto.scrypt (/home/anna/Desktop/dev/exampleShop/backend/auth.js:87:5)
    at AsyncWrap.wrap.ondone (internal/crypto/scrypt.js:43:48)

如果我使用 js,而不是属性 res 中的模拟,例如:

    const res = {
      status: function(){return this},
      send: function () {
        done()
      }
    }
}

那么我没有这个错误。

有人可以解释我有什么问题吗?

标签: javascriptjestjs

解决方案


存在范围界定问题。res没有在你调用的地方定义res.send(),因为res是在try块内定义的。

将您的期望语句移到try下面的类似内容中,或者在与您的语句res相同的范围内定义。expect

您也不能调用.toBeCalledWith不是模拟函数的函数。所以请注意,我已经定义res.send为一个模拟函数,而是done()在期望语句的末尾调用。

test('should return status 201 and msg', done => {

  try {
    const fun = handlers.handleRegister()

    // res only exists inside of the `try`
    const res = {
      status: jest.fn().mockReturnThis(),
      send: jest.fn() // << is now a mock function
    }

    fun({ body: { email: 'a', password: 'a' } }, res)

    expect(res.status).toBeCalledWith(201)

    // here `res.send` is now defined, and you can use `toBeCalledWith`
    expect(res.send).toBeCalledWith({ msg: 'User registration achieved successfully' })

    done();

  } catch(err) {
    done(err)
  }
})

推荐阅读