簡體   English   中英

你如何在Node.js中模擬MySQL(沒有ORM)?

[英]How do you mock MySQL (without an ORM) in Node.js?

我正在使用Node.js和felixge的node-mysql客戶端。 我沒有使用ORM。

我正在測試Vows並希望能夠模擬我的數據庫,可能使用Sinon。 由於我本身並沒有真正的DAL(除了node-mysql ),我真的不確定如何解決這個問題。 我的模型大多是簡單的CRUD,有很多吸氣劑。

有關如何實現這一目標的任何想法?

使用sinon,您可以在整個模塊周圍放置模擬或存根。 例如,假設mysql模塊有一個函數query

var mock;

mock = sinon.mock(require('mysql'))
mock.expects('query').with(queryString, queryParams).yields(null, rows);

queryStringqueryParams是您期望的輸入。 rows是您期望的輸出。

當您的測試類現在需要mysql並調用query方法時,它將被sinon攔截並驗證。

在您的測試期望部分,您應該:

mock.verify()

在您的拆解中,您應該將mysql恢復為正常功能:

mock.restore()

將數據庫抽象為自己的使用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});

我最后以@ 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');
       });
   });
 });
});

我不想要任何實際的數據庫連接,所以我手動模擬了所有的mysql響應。
通過向.returns添加另一個函數,您可以模擬createConnection任何方法。

我並不完全熟悉node.js,但在傳統的編程意義上,為了實現這樣的測試,你需要從數據訪問方法中抽象出來。 你不能創建一個DAL類,如:

var DataContainer = function () {
}

DataContainer.prototype.getAllBooks = function() {
    // call mysql api select methods and return results...
}

現在在測試的上下文中,在初始化期間修補getAllBooks類,如:

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

調用測試代碼時,getAllBooks將替換為返回模擬數據的版本,而不是實際調用mysql。 同樣,這是一個粗略的概述,因為我對node.js並不完全熟悉

您可以使用horaa模擬外部依賴

而且我也相信felixge的節點沙盒模塊也可以做類似的事情。

所以使用kgilpin的相同上下文,在horaa中它看起來像:

var mock = horaa('mysql');
mock.hijack('query', function(queryString, queryParam) {
    // do your fake db query (e.g., return fake expected data)
});

//SUT calls and asserts

mock.restore('query');

由於使用mysql驅動程序需要您首先創建連接,並使用返回的連接控制器的api - 您需要兩步法。

有兩種方法可以做到這一點。

存根createConnection,並讓它返回一個存根連接

在設置期間:

const sinon = require('sinon');
const mysql = require('mysql');
const {createConnection} = mysql;
let mockConnection;
sinon.stub(mysql, 'createConnection').callsFake((...args) => {
    mockConnection = sinon.stub(createConnection.apply(mysql, args))
      .expects('query').withArgs(.... )//program it how you like :)
    return mockConnection;
})

const mockConnectionFactory = 
  sinon.stub(mysql)
  .expects('createConnection')

在拆解期間:

mysql.createConnection.restore();

請注意,這里的query方法是在一個實例上進行模擬的,並且對底層的mecahnism沒有任何影響,因此只能恢復createConnection

在連接原型上存根.query方法

這種技術有點棘手,因為mysql驅動程序沒有正式公開它的導入連接。 (你可以只導入實現連接的模塊,但不能保證任何重構都不會從那里移動它)。 所以為了獲得對原型的引用 - 我通常創建一個連接並遍歷構造函數原型鏈:

我通常會在一行中完成它,但我會將其分解為步驟並在此解釋:

在設置期間:

const realConnection = mysql.createConnection({})
const mockTarget = realConnection.constructor.prototype;
//Then - brutally
consdt mock = sinon.mock(mockTarget).expect('query'....
//OR - as I prefer the surgical manner
sinon.stub(mockTarget, 'query').expect('query'....

在拆解期間

//brutal
mock.restore()
// - OR - surgical:
mockTarget.query.restore()

請注意,我們不會在這里模擬createConnection方法。 所有的連接參數驗證仍然會發生(我希望它們發生。我渴望使用最大的真實部件 - 因此模擬了獲得快速測試所需的絕對最小值)。 但是 - query在原型上被模擬,必須恢復。

另請注意,如果您通過手術工作, verify將在模擬方法上,而不在mockTarget上。

這是一個很好的資源: http//devdocs.io/sinon~6-stubs/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM