在 NodeJs 中使用 Jest 模拟 Http Post 请求

Mocking Http Post Request using Jest in NodeJs

我有一个外部 POST API 使用原生 https.request 使用 Azure Function 中的 Node Js 构建,一切正常。现在我正在尝试构建单元测试用例并且对模拟 Request 方法感到震惊。

回调响应具有 'on' 函数,需要根据值 'data' 或 'end' 进行模拟。

我收到此错误 类型错误:res.on 不是函数

下面是代码。任何建议!

index.js

'use strict';
const https = require('https')
const fs = require('fs')
const path = require('path')

module.exports = function(context, postData) {
    //Accessing Certificate and Passing along with Request Options for SSL Handshake
    var ca_path = path.join(__dirname, 'my_cert.cer');
    https.globalAgent.options.ca = fs.readFileSync(ca_path).toString()
        .split(/-----END CERTIFICATE-----\n?/)
        // may include an extra empty string at the end
        .filter(function(cert) { return cert !== ''; })
        // effectively split after delimiter by adding it back
        .map(function(cert) { return cert + '-----END CERTIFICATE-----\n'; });

    let auth = 'Basic ' + Buffer.from(process.env.username + ':' + process.env.pwd).toString('base64');

    let post_option = {
        host: process.env.host,
        path: process.env.path,
        method: 'POST',
        port: process.env.port,
        headers: {
            'Content-Type': 'application/json',
            'Authorization': auth,
            'X-Correlation-ID': JSON.parse(postData).correlationId
        }
    };

    return new Promise(function(resolve, reject) {
        var req = https.request(post_option, function(res) {
            // accumulate data
            var body = [];
            res.on('data', function(chunk) { //here is the type error
                body.push(chunk);
            });
            // resolve on end
            res.on('end', function() {
                context.log("API status: " + res.statusCode);
                if (res.statusCode == 200) {
                    try {
                        body = JSON.parse(Buffer.concat(body).toString());
                        context.log("API Success: ");
                    } catch (err) {
                        reject(err);
                    }
                    resolve(body);
                } else if (res.statusCode == 401) {
                    let errJson = {};
                    context.log("API authentication error...");
                    let err = new Error();
                    errJson["errorCode"] = res.statusCode;
                    errJson["errorMessage"] = res.statusMessage;
                    errJson["errorDescription"] = res.statusMessage;
                    err = errJson;
                    return reject(err);
                } else {
                    body = JSON.parse(Buffer.concat(body).toString());
                    context.log("API error...", body);
                    let err = new Error();

                    if (body.error != null && body.error != undefined) {
                        err = body.error;
                    } else {
                        let errJson = {};
                        errJson["errorCode"] = res.statusCode;
                        errJson["errorMessage"] = res.statusMessage;
                        errJson["errorDescription"] = res.statusMessage;
                        err = errJson;
                    }
                    return reject(err);
                }
            });
        });
        // reject on request error
        req.on('error', function(err) {
            context.log("API Generic error...", err);
            let err = new Error();
            let errJson = {};
            if (err.message && err.message.indexOf("ENOTFOUND") >= 0)
                errJson["errorCode"] = 404;
            else
                errJson["errorCode"] = 500;
            errJson["errorMessage"] = err.message;
            errJson["errorDescription"] = err.message;
            err = errJson;
            reject(err);
        });
        if (postData) {
            req.write(postData);
        }
        req.end();
    });
}

index.test.js

 var https = require('https');
    jest.mock('https', () => ({
    ...jest.requireActual('https'), // import and retain the original functionalities
    request: (post_option, cb) => cb('res')({
         on: jest.fn()
    }),
    on: jest.fn(),
    write: jest.fn(),
    end: jest.fn()
}));


 /*
jest.mock('https', () => ({
...jest.requireActual('https'), // import and retain the original functionalities
request: (post_option, cb) => cb('res')({
    on: (data, cb) => cb('data')
}),
on: jest.fn(),
write: jest.fn(),
end: jest.fn()
}));
*/

/*    jest.mock('https', () => ({
      ...jest.requireActual('https'), // import and retain the original functionalities
      request: (post_option, cb) => cb(jest.fn(() => ({
        on: jest.fn()
      }))),
      on: jest.fn(),
      write: jest.fn(),
      end: jest.fn()
      })); 
*/

/*  jest.mock('https', () => ({
    ...jest.requireActual('https'), // import and retain the original functionalities
    request: (post_option, cb) => cb({
     on: jest.fn()
    })
    })); 
*/

尝试了不同的方法来为 'on' 函数创建模拟但没有成功。

经过3个小时的实验,我终于可以模拟'res'的'on'功能了。

cb 实际上应该设置为一个需要 2 个参数的函数

这是模拟语法。

let apiResponseBody = `{
                         "status": "Dummy Request sent to API"
                       }`;
    
var https = require('https');
jest.mock('https', () => ({
 ...jest.requireActual('https'), // import and retain the original functionalities
 request: (post_option, cb) => cb({
  on: (data, cb) => cb(Buffer.from(apiResponseBody, 'utf8')),
  statusCode: 200,
  statusMessage: 'API Success'
 }),
 on: jest.fn(),
 write: jest.fn(),
 end: jest.fn()
}));