Как проверить пользовательское промежуточное ПО Koa для обработки ошибок?

В рамках миграции более старого приложения с 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);
  });
});

2 ответа

Вы можете использовать эту библиотеку https://www.npmjs.com/package/co которая используется koa.js 1.x, чтобы обернуть ваши функции генератора и макетировать объект контекста.

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))

  });
});

Вот как я решил эту проблему с 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'});
    });
});

Промежуточные программы 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, что меняет семантику, поэтому юнит-тесты не так полезны (субъективно)

Другие вопросы по тегам