Как правильно закрыть экспресс-сервер между тестами с помощью mocha и chai

Я ищу правильный способ полностью сбросить мой экспресс-сервер между тестами! Кажется, это проблема не только для меня, многие другие пользователи задавали тот же вопрос, и по этому поводу было написано много сообщений в блогах. Предложенные решения не работают и не удовлетворяют меня. Здесь есть еще один похожий вопрос и статья, которая хорошо описывает проблему и предлагает некоторые решения:

Переполнение стека Закрытие экспресс-сервера после запуска спецификаций jasmine

Блог: https://glebbahmutov.com/blog/how-to-correctly-unit-test-express-server/

Здесь специально создан пакет для решения этой проблемы: https://www.npmjs.com/package/server-destroy

Теперь минимальный рабочий пример для воспроизведения моего состояния. В тестируемом коде создается экспресс-сервер; при вызове на какой-либо конечной точке сервер увеличивает значение и возвращает его:

( function() {
   'use strict'

   const enableDestroy = require( 'server-destroy' )
   const app = require( 'express' )()
   const http = require( 'http' )

   let val = 0

   app.use( '/inc', (req, res) => {
      val ++
      res.send(val.toString())
   } )

   const server = http.createServer( app )

   server.listen( 3000 )

   enableDestroy(server);

   module.exports = server

} )()

Тест состоит из двух одинаковых тестовых случаев; оба они вызывают сервер на конечной точке и проверяют возвращаемое значение. Разделы before_each и after_each предназначены для обеспечения того, чтобы новое соединение было создано до запуска одного тестового примера, а затем закрыто, чтобы обеспечить независимость между двумя тестовыми наборами:

const chai = require( 'chai' )
const chaiHttp = require( 'chai-http' )
const expect = chai.expect
chai.use( chaiHttp )

let server

describe( 'first test group', () => {
   beforeEach( () => {
      server = require( './server' )
   } ),

   afterEach( ( done ) => {
      server.destroy( done )
      delete require.cache[require.resolve( './server' )]
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } )
} )

Тест завершается неудачно, так как сервер не работает после первого теста. Обратите внимание, что в разделе after_each я принудительно провел очистку кеша для полной потери последнего экземпляра сервера. Тест завершается успешно, если выполняется один тестовый пример:

first test group
    ✓ should respond 1
    1) "after each" hook for "should respond 1"


  1 passing (70ms)
  1 failing

  1) first test group
       "after each" hook for "should respond 1":
     Error: Not running
      at Server.close (net.js:1620:12)
      at emitCloseNT (net.js:1671:8)
      at _combinedTickCallback (internal/process/next_tick.js:135:11)
      at process._tickCallback (internal/process/next_tick.js:180:9)

Конфигурация, которую я использовал:

  • Узел 8.11.1
  • Экспресс: «4.16.3»
  • Мокко 5.0.5
  • чай 4.1.2
  • чай-http 4.0.0

Как я могу решить эту проблему? И что означает сообщение об ошибке?


person Elia    schedule 12.04.2018    source источник
comment
Обычно тестирование выполняется с помощью прокси-сервера веб-сервера, а не сервера. Есть ли причина, по которой вы не можете использовать прокси для express, например supertest?   -  person user2347763    schedule 12.04.2018
comment
Я думаю, что я использую прокси, в моем случае должно быть chai-http, не так ли?   -  person Elia    schedule 12.04.2018
comment
В afterEach вы уничтожаете сервер и вполне возможно, что перед тем, как сервер снова запустится, будет запущен второй тест. 1. Попробуйте использовать тайм-аут перед запуском второго теста. 2. Используйте keepOpen, как в const chaiHttp = chai.request(app).keepOpen() (эта строка взята из документации chai-http), чтобы сервер оставался открытым. В любом случае удалите блок afterEach и строку .set('connection',close), так как они не делают ничего полезного для ваших тестов.   -  person user2347763    schedule 12.04.2018


Ответы (2)


Он работает с небольшой модификацией, которую я не использовал server-destroy, так как server.close работает просто отлично.

сервер.js

// ( function() { // no need for this
   'use strict'

   //const enableDestroy = require( 'server-destroy' )
   const app = require( 'express' )()
   const http = require( 'http' )

   let val = 0

   app.use( '/inc', (req, res) => {
      val ++
      res.send(val.toString())
   } )

   const server = http.createServer( app )

   server.listen( 3000 )

   // enableDestroy(server);    
   module.exports = server

// } )()

test.js

const chai = require( 'chai' )
const chaiHttp = require( 'chai-http' )
const expect = chai.expect
chai.use( chaiHttp )

let server

describe( 'first test group', () => {
   beforeEach( () => {
      server = require( './server' )
   } ),

   afterEach( ( done ) => {

      // UPDATE DON'T CLOSE THE SERVER

      delete require.cache[require.resolve( './server' )]
      done()

      //server.close( () => {
      //   delete require.cache[require.resolve( './server' )]
      //   done()
      //})      
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } )
} )
person Molda    schedule 12.04.2018
comment
Благодарю вас! Я даю вам голос за эту работу, но я думаю, что это не может быть правильным решением: cb, который вы передали для закрытия, игнорирует аргументы (ошибка в этом случае). Вы можете проверить, просто записав аргумент, переданный в close cb: ошибка все еще присутствует здесь! server.close( (arg) => { console.log( arg ) delete require.cache[require.resolve( './server' )] done() }) - person Elia; 12.04.2018
comment
Вы правы, поэтому я обновил свой код. Не звони server.close. Сервер будет закрыт автоматически, я не знаю, почему. Вы можете проверить, закрывается ли он, добавив эту строку server.on( 'close', () => console.log('Closing') ) чуть ниже server.listen в файле server.js. - person Molda; 12.04.2018
comment
Да, правильно! Кажется, что кто-то закрыл сервер для меня, поэтому больше нет необходимости закрывать сервер вручную, но остаются ограничения для удаления кеша, если вы хотите обновить экземпляр сервера! Так что, если вы обновите свой ответ этой новостью, я приму это! - person Elia; 12.04.2018

это спустя некоторое время после первоначального вопроса.

В документации chai-http я смог найти механизм открытия соединения с сервером с использованием метода keepOpen() из метода запроса. (на самом деле он возвращает обратно агента)

const chai = require("chai");
const chaiHttp = require("chai-http");

chai.use(chaiHttp);

const chaiAppServer = chai.request(server).keepOpen();

https://www.chaijs.com/plugins/chai-http/#integration-testing

Но, как упоминается, мы должны вручную закрыть соединение с сервером.

describe("TEST Case group", () => {

    after(() => {
        chaiAppServer.close();
    });

    //..... integration test cases

});
person Jayanga Jayathilake    schedule 09.12.2020