4

作为将旧应用程序从 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);
  });
});
4

3 回答 3

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'});
    });
});
于 2019-04-14T21:22:32.787 回答
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))

  });
});
于 2017-04-03T12:04:10.530 回答
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);
  });
});

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

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

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

于 2016-12-01T13:27:17.820 回答