Jest测试猫鼬模型实例化

时间:2017-08-20 22:20:29

标签: javascript unit-testing express mongoose jestjs

我正在尝试测试使用express和mongoose构建的REST API,我正在使用jest和supertest进行http调用;我也是用javascript进行测试的新手。

在测试创建URL时,我不想确保仅使用req.body对象调用实例化,但在阅读了很多关于模拟对象和存根之间的差异之后我不知道该怎么做我上次尝试的Jest文档看起来像这样:

test('Should instantiate the model using req.body', done => {

  const postMock = jest.fn();

  const testPost = {
    name: 'Test post',
    content: 'Hello'
  };

  postMock.bind(Post); // <- Post is my model

  // I mock the save function so it doesn't use the db at all
  Post.prototype.save = jest.fn(cb => cb(null, testPost));

  // Supertest call
  request(app).post('/posts/')
  .send(testPost)
  .then(() => {
    expect(postMock.mock.calls[0][0]).toEqual(testPost);
    done();
  })
  .catch(err => {throw err});

});

此外,我想知道如何在承诺拒绝时手动失败测试,​​因此它不会抛出Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

1 个答案:

答案 0 :(得分:5)

目前,您正在执行更多的集成测试,而不是隔离路由处理程序函数本身并仅测试它。

首先,我会将/posts/的处理程序转移到自己的文件中(假设您还没有完成此操作):

controllers/post-controller.js

const Post = require('./path/to/models/post')

exports.store = async (req, res) => {
  const post = await new Post(req.body).save()
  res.json({ data: post }
}

接下来,只需在您定义路线的地方使用处理程序:

const express = require('express')
const app = express()
const postController = require('./path/to/controllers/post-controller')

app.post('/posts', postController.store)

通过这种抽象,我们现在可以隔离我们的postController.store并测试它是否与req.body一起使用。既然我们需要模拟mongoose以避免命中实际的数据库,你可以像这样创建一个模拟的Post(使用你已经拥有的代码):

path/to/models/__mocks__/post.js

const post = require('../post')

const mockedPost = jest.fn()
mockedPost.bind(Post)

const testPost = {
  name: 'Test post',
  content: 'Hello'
}


Post.prototype.save = jest.fn(cb => {
  if (typeof cb === 'function') {
    if (process.env.FORCE_FAIL === 'true') {
      process.nextTick(cb(new Error(), null))
    } else {
      process.nextTick(cb(null, testPost))
    }
  } else {
    return new Promise((resolve, reject) => {
      if (process.env.FORCE_FAIL === 'true') {
        reject(new Error())
      } else {
        resolve(testPost)
      }
    })
  }
})

module.exports = mockedPost

如果出于任何原因想要使其失败,请注意检查process.env.FORCE_FAIL

现在,我们已准备好使用req.body工作进行测试:

post-controller.test.js

// Loads anything contained in `models/__mocks__` folder
jest.mock('../location/to/models')

const postController = require('../location/to/controllers/post-controller')

describe('controllers.Post', () => {
  /**
   * Mocked Express Request object.
   */
  let req

  /**
   * Mocked Express Response object.
   */
  let res

  beforeEach(() => {
    req = {
      body: {}
    }
    res = {
      data: null,
      json(payload) {
        this.data = JSON.stringify(payload)
      }
    }
  })

  describe('.store()', () => {
    test('should create a new post', async () => {
      req.body = { ... }
      await postController(req, res)
      expect(res.data).toBeDefined()

      ...
    })

    test('fails creating a post', () => {
      process.env.FORCE_FAIL = true
      req.body = { ... }

      try {
        await postController.store(req, res)
      } catch (error) {
        expect(res.data).not.toBeDefined()

        ...
      }
    })

  })
})

此代码未经测试,但我希望它有助于您的测试。