如何为 chai-http 导出 node express 应用程序

How to export node express app for chai-http

我有一个带有几个端点的 Express 应用程序,目前正在使用 mocha、chai 和 chai-http 对其进行测试。在我为池化 mongo 连接添加逻辑并开始构建依赖于数据库连接的端点之前,这一切都很好。基本上,在导入 API 路由并启动应用程序之前,我想确保已连接到 mongo。

我的问题是我无法理解如何为 chai-http 导出我的应用程序,但还要确保在测试任何端点之前存在数据库连接。

在这里,我连接到 mongo,然后在回调中应用我的 API 并启动应用程序。这个例子的问题是我的测试将在连接到数据库之前以及定义任何端点之前开始。我可以将 app.listenapi(app) 移到 MongoPool.connect() 回调之外,但是当测试 运行 时我仍然遇到没有数据库连接的问题,所以我的端点将失败。

server.js

import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;
MongoPool.connect((err, success) => {
    if (err) throw err;
    if (success) {
        console.log("Connected to db.")
        // apply express router endpoints to app
        api(app);
        app.listen(port, () => {
            console.log(`App listening on port ${port}`);
        })
    } else {
        throw "Couldnt connect to db";
    }

})
export default app;

如何使用 chai-http 测试我的端点,同时确保在实际执行测试之前存在池连接?以符合我正在使用的测试的方式编写我的应用程序感觉很脏。这是我的池实现的设计问题吗?有没有更好的方法来使用 chai-http 测试我的端点?

这是我的测试 运行

test.js

let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server').default;;
let should = chai.should();


chai.use(chaiHttp);
//Our parent block
describe('Forecast', () => {
/*
  * Test the /GET route
  */
  describe('/GET forecast', () => {
      it('it should GET the forecast', (done) => {
        chai.request(server)
            .get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
            .end((err, res) => {
                res.should.have.status(200);
              done();
            });
      });
  });

});

这是我正在测试的端点

/api/forecast.js

import express from 'express';
import MongoPool from '../lib/MongoPool';
let router = express.Router();
let db = MongoPool.db();

router.get('/forecast', (req, res) => {
    // do something with DB here
})

export default router;

感谢您的帮助

只需在下面创建一个连接到 mongo 的函数,并使其成为 return 的承诺。 然后使用 await 等待它连接和 return。函数可能是这样的

function dbconnect(){
    return new Promise(function(resolve, reject){

    MongoPool.connect((err, success) => {
    if (err) reject(err);
    if (success) {
        resolve({'status' : true})
    } else {
        reject(new Error({'status' : false}))
    }

})
    })
}

然后,使用

await dbconnect();
api(app);
app.listen(port, () => {
    console.log(`App listening on port ${port}`);
})

现在 await 行将等待函数连接到数据库,然后 return 成功或失败时出错。 这是一种你可以使用的解决方案,但我不建议你这样做,我们实际上是这样做的。

create services and use those services in routes, don't write DB code directly in routes.

while writing tests for routes mock/stub those services, and test services separately in other test cases, where you just pass DB object and service will add functions on that DB objects, so in tests you can connect to DB and pass that object to those services to test functions, it will give you additional benefit, if you want to use dummy/test DB for testing you can set that in test cases.

在您的测试中使用 Before 函数,如下所示:

 describe('Forecast', () => {
  before(function(done){
   checkMongoPool(done); // this function should wait and ensure mongo connection is established.
  });
  it('/GET forecast', function(cb){
  // write test code here ...
  });
});

您可以通过以下方法检查 mongodb 连接:

方法一:检查readyState即可属性-

mongoose.connection.readyState == 0; // not connected
mongoose.connection.readyState == 1; // connected`

方法二:使用事件

mongoose.connection.on('connected', function(){});
mongoose.connection.on('error', function(){});
mongoose.connection.on('disconnected', function(){});

Express app 是 EventEmitter 的一个实例,因此我们可以轻松订阅事件。即 app 可以监听 'ready' 事件。

您的 server.js 文件如下所示,

import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;

app.on('ready', function() {
  app.listen(3000, function() {
    console.log('app is ready');
  });
});

MongoPool.connect((err, success) => {
  if (err) throw err;
  if (success) {
    console.log('Connected to db.');
    // apply express router endpoints to app
    api(app);

    // All OK - fire (emit) a ready event.
    app.emit('ready');
  } else {
    throw 'Couldnt connect to db';
  }
});

export default app;

如果您使用的是本机 mongodb 客户端,您可以实现可重用池,例如:

MongoPool.js
// This creates a pool with default size of 5
// This gives client; You can add few lines to get db if you wish
// connection is a promise
let connection;
module.exports.getConnection = () => {
  connection = MongoClient(url).connect()
}

module.exports.getClient = () => connection

Now in your test you could,
const { getConnection } = require('./MongoPool')
...
describe('Forecast', () => {
  // get client connection
  getConnection()
  ...

In your route:
...
const { getClient } = require('./MongoPool')
router.get('/forecast', (req, res) => {
    // if you made sure you called getConnection() elsewhere in your code, client is a promise (which resolves to mongodb connection pool)
    const client = getClient()
    // do something with DB here
    // then you could do something like client.db('db-name').then(//more).catch()
})

您可以使用 running server 代替 express instance

使用 private port 启动服务器,然后在 运行 服务器上进行测试。

例如:PORT=9876 node server.js

在您的测试块中,使用 chai.request('http://localhost:9876')(替换为您的协议、服务器 ip...)而不是 chai.request(server)

收到一些很好的反馈后,我发现这个解决方案最适合我,它基于 and

server.js 中,我正在连接到 mongo 池,然后在 express [=12= 上发出 'ready' 事件].然后在测试中,我可以使用 before() 等待应用程序发出 'ready' 事件。一旦发生这种情况,我就可以开始执行测试了。

server.js

import express from 'express';
import bodyParser from 'body-parser';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 5000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

(async () => {
    await MongoPool.connect();
    console.log("Connected to db.");
    require('./api').default(app);
    app.listen(port, () => {
        console.log(`Listening on port ${port}.`)
        app.emit("ready");
    });
})();

export default app;

test.js

//Require the dev-dependencies
import chai from 'chai';
import chaiHttp from 'chai-http';
import server from '../src/server'; 
let should = chai.should();
chai.use(chaiHttp);

before(done => {
  server.on("ready", () => {
    done();
  })
})
describe('Forecast', () => {
  describe('/GET forecast', () => {
    it('it should GET the forecast', (done) => {
      chai.request(server)
          .get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
          .end((err, res) => {
              res.should.have.status(200);
            done();
          });
    });
  });

});