你如何在 Node.js 中模拟 MySQL(没有 ORM)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8389149/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you mock MySQL (without an ORM) in Node.js?
提问by Josh Smith
I'm using Node.js
with felixge's node-mysql
client. I am not using an ORM.
我正在Node.js
与 felixge 的node-mysql
客户一起使用。我没有使用 ORM。
I'm testing with Vows and want to be able to mock my database, possibly using Sinon. Since I don't really have a DAL per se (aside from node-mysql
), I'm not really sure how to go about this. My models are mostly simple CRUD with a lot of getters.
我正在使用 Vows 进行测试,并希望能够模拟我的数据库,可能使用 Sinon。由于我本身并没有真正的 DAL(除了node-mysql
),我不确定如何解决这个问题。我的模型大多是简单的 CRUD,有很多 getter。
Any ideas on how to accomplish this?
关于如何实现这一点的任何想法?
采纳答案by kgilpin
With sinon, you can put a mock or stub around an entire module. For example, suppose the mysql
module has a function query
:
使用 sinon,您可以在整个模块周围放置模拟或存根。例如,假设mysql
模块有一个函数query
:
var mock;
mock = sinon.mock(require('mysql'))
mock.expects('query').with(queryString, queryParams).yields(null, rows);
queryString
, queryParams
are the input you expect. rows
is the output you expect.
queryString
,queryParams
是您期望的输入。rows
是您期望的输出。
When your class under test now require mysql and calls the query
method, it will be intercepted and verified by sinon.
当你的被测类现在需要mysql并调用该query
方法时,会被sinon拦截验证。
In your test expectation section you should have:
在你的测试期望部分你应该有:
mock.verify()
and in your teardown you should restore mysql back to normal functionality:
在您的拆解中,您应该将 mysql 恢复到正常功能:
mock.restore()
回答by Bijou Trouvaille
It may be a good idea to abstract away your database into its own class which uses mysql. Then you can pass that class' instance to your model's constructors instead of them loading it using require().
将您的数据库抽象到它自己的使用 mysql 的类中可能是一个好主意。然后,您可以将该类的实例传递给模型的构造函数,而不是使用 require() 加载它。
With this set up you can pass a mock db instance to your models inside your unit test files.
通过此设置,您可以将模拟数据库实例传递给单元测试文件中的模型。
Here's a small example:
这是一个小例子:
// 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});
回答by cameck
I ended up starting with @kgilpin's answer and ended up with something like this to test Mysql in an AWS Lambda:
我最终从@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');
});
});
});
});
I didn't want any actual database connections so I manually mocked all the mysql responses.
By added another function to .returns
you can mock any method off of createConnection
.
我不想要任何实际的数据库连接,所以我手动模拟了所有 mysql 响应。
通过添加另一个函数,.returns
您可以模拟createConnection
.
回答by doogle
I'm not entirely familiar with node.js, but in a traditional programming sense, to achieve testing like that, you'd need to abstract away from the data access method. Couldn't you create a DAL class like:
我对 node.js 并不完全熟悉,但在传统的编程意义上,要实现这样的测试,您需要从数据访问方法中抽象出来。你不能创建一个 DAL 类,如:
var DataContainer = function () {
}
DataContainer.prototype.getAllBooks = function() {
// call mysql api select methods and return results...
}
Now in the context of a test, patch your getAllBooks class during initialization like:
现在在测试的上下文中,在初始化期间修补您的 getAllBooks 类,例如:
DataContainer.prototype.getAllBooks = function() {
// Here is where you'd return your mock data in whatever format is expected.
return [];
}
When the test code is called, getAllBooks will be replaced with a version that returns mock data instead of actually calling mysql. Again, this is a rough overview as I'm not entirely familiar with node.js
调用测试代码时,getAllBooks 将替换为返回模拟数据的版本,而不是实际调用 mysql。同样,这是一个粗略的概述,因为我并不完全熟悉 node.js
回答by dule
You can mock out external dependencies using horaa
And I also believe felixge's node sandboxed-modulecan also do something similar.
而且我也相信 felixge 的 node sandboxed -module也可以做类似的事情。
So using kgilpin's same context, in horaa it would look something like:
所以使用 kgilpin 的相同上下文,在 horaa 中它看起来像:
var mock = horaa('mysql');
mock.hiHyman('query', function(queryString, queryParam) {
// do your fake db query (e.g., return fake expected data)
});
//SUT calls and asserts
mock.restore('query');
回答by Radagast the Brown
Since using the mysql driver requires you first to create a connection, and use apis of the returned connection controller - you need a two step approach.
由于使用 mysql 驱动程序需要您首先创建一个连接,并使用返回的连接控制器的 api - 您需要一个两步的方法。
There are two ways to do that.
有两种方法可以做到这一点。
stubbing the createConnection, and have it return a stubbed connection
存根 createConnection,并让它返回一个存根连接
During setup:
在设置过程中:
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')
During Teardown:
拆解期间:
mysql.createConnection.restore();
Note that here the query
method is mocked on an instance, and has no implication on the underlying mecahnism, so only the createConnection
must be restored.
请注意,这里的query
方法是在一个实例上模拟的,并且对底层机制没有任何影响,因此只有createConnection
必须恢复。
stubbing the .query method on the connection prototype
在连接原型上存根 .query 方法
This technique is a little more tricky, because the mysql
driver does not expose officially it's connection for import. (well you could just import just the module implementing the connection, but there's no guarantee that any refactoring won't move it from there).
So in order to obtain a reference to the prototype - I usually create a connection and traverse up the constructor-prototype chain:
这种技术有点棘手,因为mysql
驱动程序没有正式公开它的导入连接。(好吧,您可以只导入实现连接的模块,但不能保证任何重构都不会从那里移动它)。因此,为了获得对原型的引用 - 我通常会创建一个连接并向上遍历构造函数-原型链:
I usually do it in one line, but I'll break it down to steps and explain it here:
我通常在一行中完成,但我会将其分解为步骤并在此处进行解释:
During setup:
在设置过程中:
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'....
During Teardown
拆解期间
//brutal
mock.restore()
// - OR - surgical:
mockTarget.query.restore()
Note that we don't mock the createConnection
method here. All the connection-parameter validations will still happen (which I want them to happen. I aspire to work with maximum authentic parts - hence mock the absolute minimum required to get a fast test). However - the query
is mocked on the prototype, and must be restored.
请注意,我们不会在createConnection
此处模拟该方法。所有连接参数验证仍然会发生(我希望它们发生。我渴望使用最多的真实部件 - 因此模拟获得快速测试所需的绝对最小值)。但是 - 在query
原型上被模拟,并且必须恢复。
Also note that if you work surgically, the verify
will be on the mocked method, not on the mockTarget.
另请注意,如果您通过手术进行工作,verify
则将在模拟方法上,而不是在 mockTarget 上。
Here's a good resource about it: http://devdocs.io/sinon~6-stubs/
这是一个很好的资源:http: //devdocs.io/sinon~6-stubs/