如何测试自定义Koa中间件以进行错误处理?

问题描述 投票:2回答:3

作为将旧应用程序从ExpressJs迁移到Koa JS(v1)的一部分。我编写了一个中间件来处理发生的任何错误。它看起来像这样:

module.errors = function * (next) {
  try {
    yield next;
  } catch (err) {
    switch(err && err.message) {
      case: 'Bad Request':
        this.status = 400;
        this.body = {message: 'Bad Request'};
        brea;
      default:
        this.status = 500;
        this.body = {message: 'An error has occurred'};
    }

    this.app.emit('error', err, this);
  }
}

它包含在我的应用程序中,如下所示:

const app = require('koa')();
const router = require('koa-router');
const { errors } = require('./middleware/errors');

app.use(errors)
   .use(router.routes());

app.get('/some-request', function *(next){
  // request that could error
});

app.listen();

这一切都很好,但我想用我的单元测试来测试中间件,也许是因为我对Koa和Generator函数都还不太新,我很难弄清楚如何做到这一点。

我知道如果我导入错误处理中间件,我需要传递一个会抛出错误的函数,但是如何执行传递的函数?是否需要关闭一些描述?如何断言/期望为状态代码等设置的值?

const { expect } = require('chai');
const { errors } = require('../middleware/errors');

describe('errors middleware', () => {

  it('returns a 500 on a generic error', () => {
      let thrower = function(){ throw new Error() }
      let errorHandler = errors(thrower());

      // mass of confusion

      expect(errorHandler.next()).to.throw(Error);
  });
});
javascript node.js koa koa-router
3个回答
0
投票

Koa中间件是生成器(多次返回/生成)并且不像函数那样,因此为它们编写单元测试感觉很奇怪。就个人而言,我只需要端到端的测试用例。

但是,以下情况可能适用于您的情况。

const { expect } = require('chai');
const { errors } = require('../middleware/errors');

describe('errors middleware', () => {

  it('returns a 500 on a generic error', () => {

      let ctx = { body: {}, status: 404 };

      let errorMidIterator = errors().call(ctx, 'NEXT_MID');

      // test that it correctly yields to next middleware
      expect(errorMidIterator.next().value).should.equal('NEXT_MID');

      // simualte an error and test if it correctly sets the body
      expect(errorMidIterator.throw(new Error()).done).to.equal(true);
      expect(ctx.status).should.equal(500);
  });
});

作为旁注,我认为最好从文件中导出中间件工厂,而不是普通的中间件生成器函数。前者为您提供更多控制(即您可以通过Factory函数参数注入一些依赖项,在本例中为thrower()函数)。我的中间件文件看起来像这些。

module.exports = function MyMiddleware(options) {
   return function *MyMiddleware(next) {
      // options.config.foo
      // options.httpclient.get(..)
   };
}

最后koa用co包装生成器函数,这改变了语义,因此单元测试不是那么有用(主观)


0
投票

您可以使用koa.js 1.x使用的库https://www.npmjs.com/package/co来包装您的生成器函数并模拟上下文对象。

const co = require('co');
const Emitter = require('events');
const { expect } = require('chai');
const { errors } = require('../middleware/errors');

const wrapped = co.wrap(errors);
const mockApp = new Emitter();

describe('errors middleware', () => {

  it('returns a 500 on a generic error', (done) => {
    const ERROR_MSG = 'middleware error';
    const ctx = {app: mockApp};
    const next = function* () {
      throw new Error(ERROR_MSG);
    }
    wrapped.call(ctx, next)
      .then(() => {
        try {
          expect(ctx.status).to.equal(500);
          expect(ctx.body.message).to.equal(ERROR_MSG);
          done();
        } catch (err) {
          done(err);
        }
      })
      .catch(err => done(err))

  });
});

0
投票

这就是我用Jest解决这个问题的方法,我刚创建了一个自定义res对象并将其传递给错误处理程序:

const error = require('../../../middleware/error');

describe('error middleware', () => {
    it(' return 500 if there is unhandled error', async () => {
        const res = {
            status: (c) => {this.c = c; return {send: (s) => {this.s = s; return this}}} ,
            c: 200,
            s: 'OK',
        };
        const req = {};
        const next = jest.fn();
        const err = () => {
            throw new Error()
        };
        const errorHandler = error(err, req, res, next);
        expect(errorHandler).toMatchObject({c: 500, s: 'Something failed'});
    });
});
© www.soinside.com 2019 - 2024. All rights reserved.