我如何对HTTP请求进行单元测试?

问题描述 投票:5回答:4

正如标题所问,如何使用Mocha和Chai测试HTTP请求?

我最近开始学习单元测试,但我仍然对测试的某些方面感到困惑。我可以通过返回值的精细测试方法获得结果,但是我对如何测试发出HTTP / IO请求的方法感到困惑。

例如,我有以下代码:

module.exports = someRequest => new Promise((resolve, reject) => 
    http.get('http://google.com', resp => {
        if(resp.headers['content-type'] !== 200) {
            reject(new Error('Failed to connect to Google'));
        }
        resolve('Connected to Google');
    })
);

我要测试2个案例:

  1. 对Google的请求成功
  2. 对Google的请求失败

我是否必须模拟这些请求,如果是,则模拟用于发出HTTP请求的方法的目的是什么?

node.js unit-testing mocha chai
4个回答
3
投票

我过去使用过supertest,对此非常满意

import server from '../src/server';
import Request from 'supertest';

describe('Server', () => {
  const request = Request(server());

  describe('/api', () => {
    it('Should return a 404 when invalid route', done => {
      request
        .post('/api/notfound')
        .expect(404)
        .end(done);
    });
  });
});

3
投票

尝试Mockttp(免责声明:我是Mockttp的维护者)。

Mockttp为您提供了一个真正的本地HTTP服务器和代理,您可以轻松地将其配置并用于测试,在这些情况下。

[您可以使用本地主机URL直接向Mockttp发出请求,也可以继续向google.com发出请求,但将Mockttp用作代理,然后配置您要模拟的任何响应(或失败的连接/超时)。

自述文件中有一些示例准确显示了您要查找的内容:https://www.npmjs.com/package/mockttp#get-testing。在您的特定情况下,我会尝试类似的方法:

const makeRequest = require("./your-code");
const mockServer = require("mockttp").getLocal();

describe("makeRequest", () => {
    beforeEach(() => mockServer.start());
    afterEach(() => mockServer.stop());

    it("resolves happily for successful requests", () => {
        await mockServer.get("http://google.com").thenReply(200, "A mocked response");

        // Here: configure your code to use mockServer.url as a proxy

        let result = await makeRequest();

        expect(response).to.equal('Connected to google');
    });

    it("rejects failed requests with an error", () => {
        await mockServer.get("http://google.com").thenReply(500, "An error!");

        // Here: configure your code to use mockServer.url as a proxy

        let result = makeRequest();

        expect(response).to.be.rejectedWith('Failed to connect to Google');
    });
});

配置代理将取决于您使用的库,但是对于http.get https://www.npmjs.com/package/global-tunnel应该这样做,或者您可以将{ proxy: mockServer.url }选项直接传递给http.get调用。或者当然,您可以使http://google.com URL可配置,将其设置为mockServer.url,然后模拟/而不是http://google.com

主要区别在于这是一个真正的集成测试。您不是在JS级别上进行嘲笑,而是在发送真实的HTTP请求,测试实际发送和接收的内容,以及实际上您的完整Node +应用程序代码将如何响应。

您可以完全在JS级别上进行模拟,并且您的测试将稍微快一些(在2ms到10ms的范围内),但是很容易获得不准确的结果,并且可以通过测试,但是破坏了真实世界的功能。


2
投票

如何用类似的东西嘲笑http.get呢?

const createHttpGetMock = (expectedStatus) => {
  return httpGetMock = (address) => {
    return new Promise((resolve, reject) => {
      resolve({
        status: expectedStatus,
        headers: {
          // ... headers
        },
        // mock response
      })
    })
  }
}

然后您的测试可能如下所示:

describe("Google request", () => {
  it("Resolves when google responds", async () => {
    const httpSuccessMock = createHttpGetMock(200);
    // Inject your mock inside your request function here, using your favorite lib

    const message = await fetchGoogle();
    assert.equals(message, 'Connected to Google');
  })

  it("Rejects when google responds with error", async () => {
    const httpSuccessMock = createHttpGetMock(500);
    // Inject your mock inside your request function here, using your favorite lib

    const message = await fetchGoogle();
    assert.equals(message, 'Failed to connect to Google');
  })
});

这将满足良好的单元测试的基本要求:无论外部模块和依赖项如何,它都会确保您当前正在测试的模块在每种可能的情况下均具有正确的行为。


2
投票

这里是mochachai的测试示例。我们还需要sinon存根http库。

http://sinonjs.org/releases/v1.17.7/stubs/

// test.js

const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const http = require('http');

const someRequest = require('./index');

describe('some request test', function() {
  let httpGetStub;

  beforeEach(function() {
    httpGetStub = sinon.stub(http, 'get'); // stub http so we can change the response
  });

  afterEach(function() {
    httpGetStub.restore();
  });

  it('responses with success message', function() {
    httpGetStub.yields({ headers: { 'content-type': 200 }}); // use yields for callback based function like http.get
    return someRequest().then(res => { 
      expect(res).to.equal('Connected to Google');      
    });
  });

  it('rejects with error message', function() {
    httpGetStub.yields({ headers: { 'content-type': 400 }});
    return someRequest().catch(err => { 
      expect(err.message).to.equal('Failed to connect to Google');      
    });
  });
})

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.