繁体   English   中英

Node.js-如何处理测试文件中的异步模拟?

[英]Node.js - how to deal with async mocks in test files?

目前,我正在学习如何测试节点模块。 最近几天,我在StackOverflow上问了几个有关如何模拟节点模块以测试的问题,例如,对promise的.then()子句进行了什么处理。 我从社区中获得了一些有关如何解决此问题的好建议,并且我取得了很大的成就。 但是,仍有一些我无法解决的问题,这与异步调用有关。

例如,我目前有以下代码来添加帖子:

const makeRequestStructure = require('./modules/makeRequestStructure.js').makeRequestStructure
const normalizeFinalResponse = require('./modules/normalizeFinalResponse.js').normalizeFinalResponse
const doARequest = require('./modules/doARequest.js').doARequest

exports.addPost = (event) => {
  const requestStructure = makeRequestStructure('POST', '/posts')

  const requestPostData = {
    title: event.body.title,
    content: event.body.content
  }

  return doARequest(requestStructure, requestPostData).then((res) => {
    const finalResponse = normalizeFinalResponse(200, res)
    return finalResponse
  }).catch((err) => {
    const finalResponse = normalizeFinalResponse(400, err)
    return finalResponse
  })
}

运行此文件所需的辅助功能是:

makeRequestStructure.js (位于./modules/makeRequestStructure.js

require('dotenv').config()
const { HOST, PORT } = process.env

module.exports.makeRequestStructure = function (method, path) {
  return {
    host: HOST,
    port: PORT,
    method: method,
    path: path
  }
}

该模块使用环境变量,我在.env文件中配置的是

HOST=jsonplaceholder.typicode.com
POST=433

接下来,我有normalizeFinalResponse.js文件和doARequest.js文件:

normalizeFinalResponse.js (位于./modules/normalizeFinalResponse.js

module.exports.normalizeFinalResponse = function (statusCode, message) {
  return {
    'statusCode': statusCode,
    'body': { message: message }
  }
}

doARequest.js (位于./modules/doARequest.js

const https = require('https')

module.exports.doARequest = function (params, postData) {
  return new Promise((resolve, reject) => {
    const req = https.request(params, (res) => {
      let body = []
      res.on('data', (chunk) => {
        body.push(chunk)
      })
      res.on('end', () => {
        try {
          body = JSON.parse(Buffer.concat(body).toString())
        } catch (e) {
          reject(e)
        }
        resolve(body)
      })
    })
    req.on('error', (err) => {
      reject(err)
    })
    if (postData) {
      req.write(JSON.stringify(postData))
    }
    req.end()
  })
}

现在,此代码非常简单。 通过运行以下文件,它将对jsonplaceholder.typicode.com:433/posts进行POST调用,其正文{ body: { title: 'Lorem ipsum', content: 'Lorem ipsum dolor sit amet' } }

const addPost = require('./addPost.js').addPost;
const event = { body: { title: 'Lorem ipsum', content: 'Lorem ipsum dolor sit amet' } }

addPost(event).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

addPost模块中,调用函数normalizeFinalResponse来标准化来自jsonplaceholder api的响应。 为了检查这一点,我创建了以下测试文件。

//Dependencies
const mock = require('mock-require')
const sinon = require('sinon')
const expect = require('chai').expect

//Helper modules
const normalizeFinalResponse = require('../modules/normalizeFinalResponse.js')
const doARequest = require('../modules/doARequest.js')

//Module to test
const addPost = require('../addPost.js')

//Mocks
const addPostReturnMock = { id: 101 }

describe('the addPost API call', () => {
  it('Calls the necessary methods', () => {

    console.log(1)

    //Mock doARequest so that it returns a promise with fake data.
    //This seems to be running async. The test file continues to run when its not resolved yet
    mock('../modules/doARequest', { doARequest: function() {
      console.log(2)
      return Promise.resolve(addPostReturnMock);
    }});

    console.log(3)

    //Stub functions expected to be called
    let normalizeFinalResponseShouldBeCalled = sinon.spy(normalizeFinalResponse, 'normalizeFinalResponse');

    //Set a fake eventBody
    let event = { body: { title: 'Lorem ipsum', content: 'Lorem ipsum dolor sit amet' } }

    //Call the method we want to test and run assertions
    return addPost.addPost(event).then((res) => {
      expect(res.statusCode).to.eql(200);
      sinon.assert.calledOnce(normalizeFinalResponseShouldBeCalled);
    })
  });
});

运行该测试文件会使断言失败,因为显然从未调用过normalizeFinalResponse函数。 当我使用console.log时,它们以1,3,2的顺序打印。 这使我相信, mock()函数尚未完成,因此它将对jsonplaceholder api进行实际调用。 但是比起仍然应该调用函数normalizeFinalResponse吧?

我有一种感觉,就是我忽略了眼前的事物。 但是我不知道这是什么。 如果您知道我的考试有什么毛病,我很想听听。 它可以帮助我更好地理解编写此类测试。

我的package.json供参考:

{
  "name": "mock-requests-tests",
  "version": "0.0.1",
  "description": "A test repository so i can learn how to mock requests",
  "scripts": {
    "test": "mocha --recursive tests/",
    "test:watch": "mocha --recursive --watch tests/"
  },
  "devDependencies": {
    "chai": "^4.1.2",
    "mock-require": "^3.0.2",
    "sinon": "^7.2.2"
  }
}

从来没有调用过normalizeFinalResponse间谍的原因是addPost直接调用了该方法,而不使用本地创建的间谍。

当您调用sinon.spy()将创建一个直通函数来跟踪是否执行了此新方法。 这是设计使然,因为您不希望代码不断从下面改变您的功能。

一般来说,您根本不会在乎是否已执行normalizefinalResposne 您唯一需要关注的是,给定输入addPost(x)会返回y ,内部细节并不重要,只要输入x得到结果y 这也简化了以后的重构,因为只有在功能按预期停止运行时,单元测试才会中断,这不仅仅是因为代码看起来不同而功能保持不变。

该规则有一些例外,主要是在使用第三方代码时。 这样的示例之一在doARequest 您可能想使用模拟库来覆盖http模块,以便它返回精心制作的响应,并且不会发出实时网络请求。

暂无
暂无

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

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