你如何在Node.js中模拟MySQL(没有ORM)?

Jos*_*ith 41 mysql mocking node.js sinon vows

我正在使用Node.jsfelixge的node-mysql客户端.我没有使用ORM.

我正在测试Vows并希望能够模拟我的数据库,可能使用Sinon.由于我本身并没有真正的DAL(除了node-mysql),我不确定如何解决这个问题.我的模型大多是简单的CRUD,有很多吸气剂.

有关如何实现这一目标的任何想法?

kgi*_*pin 36

使用sinon,您可以在整个模块周围放置模拟或存根.例如,假设mysql模块具有以下功能query:

var mock;

mock = sinon.mock(require('mysql'))
mock.expects('query').with(queryString, queryParams).yields(null, rows);
Run Code Online (Sandbox Code Playgroud)

queryString,queryParams是你期望的输入.rows是你期望的输出.

当您的测试类现在需要mysql并调用该query方法时,它将被sinon拦截并验证.

在您的测试期望部分,您应该:

mock.verify()
Run Code Online (Sandbox Code Playgroud)

在您的拆解中,您应该将mysql恢复为正常功能:

mock.restore()
Run Code Online (Sandbox Code Playgroud)

  • .with()似乎已弃用.withArgs()。我正在用sinon 1.7.2进行测试 (2认同)

Bij*_*lle 9

将数据库抽象为自己的使用mysql的类可能是个好主意.然后,您可以将该类的实例传递给模型的构造函数,而不是使用require()加载它.

通过此设置,您可以将模拟数据库实例传递到单元测试文件中的模型.

这是一个小例子:

// db.js
var Db = function() {
   this.driver = require('mysql');
};
Db.prototype.query = function(sql, callback) {
   this.driver... callback (err, results);
}
module.exports = Db;

// someModel.js
var SomeModel = function (params) {
   this.db = params.db
}
SomeModel.prototype.getSomeTable (params) {
   var sql = ....
   this.db.query (sql, function ( err, res ) {...}
}
module.exports = SomeModel;

// in app.js
var db = new (require('./db.js'))();
var someModel = new SomeModel ({db:db});
var otherModel = new OtherModel ({db:db})

// in app.test.js
var db = {
   query: function (sql, callback) { ... callback ({...}) }
}
var someModel = new SomeModel ({db:db});
Run Code Online (Sandbox Code Playgroud)


doo*_*gle 5

我并不完全熟悉node.js,但在传统的编程意义上,为了实现这样的测试,你需要从数据访问方法中抽象出来.你不能创建一个DAL类,如:

var DataContainer = function () {
}

DataContainer.prototype.getAllBooks = function() {
    // call mysql api select methods and return results...
}
Run Code Online (Sandbox Code Playgroud)

现在在测试的上下文中,在初始化期间修补getAllBooks类,如:

DataContainer.prototype.getAllBooks = function() {
    // Here is where you'd return your mock data in whatever format is expected.
    return [];
}
Run Code Online (Sandbox Code Playgroud)

调用测试代码时,getAllBooks将替换为返回模拟数据的版本,而不是实际调用mysql.同样,这是一个粗略的概述,因为我对node.js并不完全熟悉


cam*_*eck 5

我最后以@ kgilpin的答案开始,最后用这样的东西来测试AWS Lambda中的Mysql:

const sinon = require('sinon');
const LambdaTester = require('lambda-tester');
const myLambdaHandler = require( '../../lambdas/myLambda' ).handler;
const mockMysql = sinon.mock(require('mysql'));
const chai = require('chai');
const expect = chai.expect;

describe('Database Write Requests', function() {

 beforeEach(() => {
   mockMysql.expects('createConnection').returns({
     connect: () => {
       console.log('Succesfully connected');
     },
     query: (query, vars, callback) => {
       callback(null, succesfulDbInsert);
     },
     end: () => {
       console.log('Connection ended');
     }
   });

 });
 after(() => {
   mockMysql.restore();
 });

 describe( 'A call to write to the Database with correct schema', function() {

   it( 'results in a write success', function() {

     return LambdaTester(myLambdaHandler)
       .event(anObject)
       .expectResult((result) => {
         expect(result).to.equal(succesfulDbInsert);
       });
   });
 });


 describe( 'database errors', function() {

   before(() => {
     mockMysql.expects('createConnection').returns({
       connect: () => {
         console.log('Succesfully connected');
       },
       query: (query, vars, callback) => {
         callback('Database error!', null);
       },
       end: () => {
         console.log('Connection ended');
       }
     });
   });

   after(() => {
     mockMysql.restore();
   });

   it( 'results in a callback error response', function() {


     return LambdaTester(myLambdaHandler)
       .event(anObject)
       .expectError((err) => {
         expect(err.message).to.equal('Something went wrong');
       });
   });
 });
});
Run Code Online (Sandbox Code Playgroud)

我不想要任何实际的数据库连接,所以我手动模拟了所有的mysql响应.
通过添加另一个函数,.returns你可以模拟任何方法createConnection.

  • 干得好 - 然而 - 你可以再采取一步:你很好地模拟了静态 createConnection api,但你返回了一个手动创建的连接实例,并失去了 sinon 的力量。我将保存对原始 createConnection 的引用,对其进行存根,并使用stub#callsFake对其进行编程,并且在假中我将生成一个连接并存根它,如 kgilpin 的示例中所示。 (2认同)