英文:
Mocking a connection call to AWS DynamoDB in sinon
问题
Handler.js 中的 saveUser
方法似乎在测试中仍然尝试连接到 DynamoDB。这可能是因为 db.put
函数返回了一个 Promise 以及在回调中使用了它。您可以尝试将 db.put
函数的 stub 更改为以下形式,以确保它返回一个正确的 Promise:
sandbox.stub(db, "put").callsFake((params, callback) => {
callback(null, { statusCode: 200 });
});
这将模拟 DynamoDB 的 put
方法并返回一个成功的响应,而不会尝试实际连接到 DynamoDB。这应该解决您的测试问题。
英文:
I have an endpoint in API Gateway which is mapped to a Lambda function in AWS. While writing test cases for the new handler function of the endpoint, I do not want the spec file to call the actual API or connect to DynamoDB. I tried to add a sinon.stub
, but it still made the call to connect to DynamoDB and the test-case failed. I am unable to locate where the stub went wrong.
Handler.js:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
saveUser(userName, logger) {
const Item = {
id: uuid.v4(),
userName,
ttl: parseInt(Date.now() / 1000) + 900 // expire the name after 15 minutes from now
};
const params = {
TableName: "my-table-name",
Item
};
logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);
return new Promise(function(resolve, reject) {
db.put(params, function(err, _) {
if (err) {
logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
reject({
statusCode: 404,
err
});
} else {
logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
resolve({
statusCode: 201,
body: Item
});
}
});
});
}
<!-- end snippet -->
Handler.spec.js:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
import AWS from "aws-sdk";
const db = new AWS.DynamoDB.DocumentClient({
apiVersion: "2012-08-10"
});
describe("user-name-handler", function() {
const sandbox = sinon.createSandbox();
afterEach(() => sandbox.restore());
it("Test saveUser() method", async function(done) {
const {
saveUser
} = userHandler;
sandbox.stub(db, "put")
.returns(new Promise((resolve, _) => resolve({
statusCode: 200
})));
try {
const result = await saveUser("Sample User", {
log: () => {},
exception: () => {}
});
expect(result).to.be.equal({
data: "some data"
});
done();
} catch (err) {
console.log(err);
done();
}
});
});
<!-- end snippet -->
Error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
I console-logged the err
object and it gave me this error, which made me think it is trying to connect to the DynamoDB.
Error: connect ENETUNREACH 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1144:16) {
message: 'Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1',
errno: 'ENETUNREACH',
code: 'CredentialsError',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
time: 2023-05-07T10:45:25.835Z,
originalError: {
message: 'Could not load credentials from any providers',
errno: 'ENETUNREACH',
code: 'CredentialsError',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
time: 2023-05-07T10:45:25.835Z,
originalError: [Object]
}
答案1
得分: 1
你正在嘲笑测试文件中声明的db
,而不是saveUser
实际使用的db
。
解决方案是将db
声明移到它自己的模块中,例如:db.js
const AWS = require("aws-sdk");
const db = new AWS.DynamoDB.DocumentClient({
apiVersion: "2012-08-10"
});
module.exports = db;
然后从saveUser
模块和测试模块都导入它,这样我们将模拟与saveUser
使用的相同db
实例。
更新:我成功运行了以下代码的测试:
测试代码:
const sinon = require('sinon');
const { saveUser } = require('../userHandler');
const { expect } = require('chai');
const db = require('../db');
describe('user-name-handler', function() {
afterEach(() => sinon.restore());
it('Test saveUser() method', async function() {
sinon.stub(db, 'put')
.returns(new Promise((resolve, _) => resolve({
statusCode: 201,
body: 'some data'
})));
try {
const result = await saveUser(db, 'Sample User', {
log: () => {},
exception: () => {}
});
expect(result).to.deep.equal({
statusCode: 201,
body: 'some data'
});
} catch (err) {
console.log('err', err);
}
});
});
userHandler 文件:
const db = require('./db');
const saveUser = (db, userName, logger) => {
const Item = {
id: uuid.v4(),
userName,
ttl: parseInt(Date.now() / 1000) + 900 // 在当前时间的15分钟后过期名称
};
const params = {
TableName: "my-table-name"
};
logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);
return db.put(params, function(err, _) {
if (err) {
logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
return reject({
statusCode: 404,
err
});
} else {
logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
return resolve({
statusCode: 201,
body: Item
});
}
});
}
module.exports = { saveUser };
这些代码示例中的注释和错误处理需要进一步处理。希望这能帮助你。
英文:
You are mocking the db
that is declared in the test file - not the db
that saveUser
is actually using.
The solution is to move the db declaration to its own module, say: db.js
const AWS = require("aws-sdk");
const db = new AWS.DynamoDB.DocumentClient({
apiVersion: "2012-08-10"
});
module.exports = db;
and then import it both from the module of saveUser
and from the test - so that we'll be mocking the same db
instance that saveUser
uses.
UPDATE
I was able to run the test with the following code successfully:
The test code:
const sinon = require('sinon');
const { saveUser } = require('../userHandler');
const { expect } = require('chai');
const db = require('../db');
describe('user-name-handler', function() {
afterEach(() => sinon.restore());
it('Test saveUser() method', async function() {
sinon.stub(db, 'put')
.returns(new Promise((resolve, _) => resolve({
statusCode: 201,
body: 'some data'
})));
try {
const result = await saveUser(db, 'Sample User', {
log: () => {},
exception: () => {}
});
expect(result).to.deep.equal({
statusCode: 201,
body: 'some data'
});
} catch (err) {
console.log('err', err);
}
});
});
userHandler file:
const db = require('./db');
const saveUser = (db, userName, logger) => {
const Item = {
id: uuid.v4(),
userName,
ttl: parseInt(Date.now() / 1000) + 900 // expire the name after 15 minutes from now
};
const params = {
TableName: "my-table-name"
};
logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);
return db.put(params, function(err, _) {
if (err) {
logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
return reject({
statusCode: 404,
err
});
} else {
logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
return resolve({
statusCode: 201,
body: Item
});
}
});
}
module.exports = { saveUser };
package.json
{
"name": "play",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha --timeout 5000"
},
"author": "",
"license": "ISC",
"dependencies": {
"aws-sdk": "^2.1373.0",
"chai": "^4.3.7",
"mocha": "^10.2.0",
"sinon": "^15.0.4"
}
}
答案2
得分: 0
# 在一个文件中分离DB连接
我们可以将DB连接分离到一个不同的文件中,并在处理程序实现以及*spec*文件中导入它。
**db.js**
```js
import AWS from "aws-sdk";
const db = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });
export default db;
yields()
函数
在存根直接返回Promise
的地方,应该与它的回调一起链接一个.yields()
。我们可以更改参数以覆盖代码的各个分支。
代码
describe("user-handler connection success", function () {
const sandbox = sinon.createSandbox();
afterEach(() => sandbox.restore());
before(() => {
sinon.stub(db, "put")
.yields(null, true);
sinon.stub(db, "get")
.yields(null, { sampleKey: "sample value" });
sinon.stub(db, "delete")
.yields(null, { sampleKey: "sample value" });
});
after(() => {
db.put.restore();
db.get.restore();
db.delete.restore();
});
it("Test saveUser() method success", async function () {
const result = await userHandler.saveToken("sample user", {
log: () => {},
exception: () => {}
});
expect(result.statusCode).to.be.equal(201);
});
});
有用链接
https://www.youtube.com/watch?v=vXDbmrh0xDQ
<details>
<summary>英文:</summary>
# Separating the DB connection in a file
We can separate the DB connection in a different file and import it in the handler implementation as well as in the *spec* file.
**db.js**
```js
import AWS from "aws-sdk";
const db = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });
export default db;
The yields()
function
Instead of the stub directly return a Promise
, it should be chained with a .yields()
along with the parameters its callback will accept. We can change the parameters to cover various branches of the code.
Code
describe("user-handler connection success", function () {
const sandbox = sinon.createSandbox();
afterEach(() => sandbox.restore());
before(() => {
sinon.stub(db, "put")
.yields(null, true);
sinon.stub(db, "get")
.yields(null, { sampleKey: "sample value" });
sinon.stub(db, "delete")
.yields(null, { sampleKey: "sample value" });
});
after(() => {
db.put.restore();
db.get.restore();
db.delete.restore();
});
it("Test saveUser() method success", async function () {
const result = await userHandler.saveToken("sample user", {
log: () => {},
exception: () => {}
});
expect(result.statusCode).to.be.equal(201);
});
});
Useful Link
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论