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

Bug*_*rUK 8 unit-testing mocha.js node.js chai

正如标题所问我如何使用 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');
    })
);
Run Code Online (Sandbox Code Playgroud)

我想测试两种情况:

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

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

piz*_*r0b 7

我过去使用过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);
    });
  });
});
Run Code Online (Sandbox Code Playgroud)


dee*_*wan 5

mocha这是使用和进行测试的示例chai。我们还需要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');      
    });
  });
})
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你。


小智 4

用类似的东西来模拟 http.get 怎么样?

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

那么你的测试可能如下所示:

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');
  })
});
Run Code Online (Sandbox Code Playgroud)

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