- How to use mocha+sinon for unit testing node.js source
- Stubbing methods in sinon
- Stubbing methods within methods in sinon
- Stubbing properties for object keys in sandbox
- Stubbing the AWS-SDK(CommonJS)
- Error in sinon
- How to stub process.env (environment variable)
- Stubbing objects
- Dynamically changing the return value of a method
- Difference between sandbox.stub().returns, resolves and yields
- SINON Assertions
- Return value memo
- Mocking a class that is new
- Related
How to use mocha+sinon for unit testing node.js source
I think there are cases like testing node.js source with mocha and stubbing it with sinon.
There is English documentation for sinon, and since it is a widely used module, stub methods can be found all over the Internet.
Stubbing methods in sinon
I would like to try sinon with a simple source.
Save the following as index.js
function testMethod(money=0){ money = money * 1.08; return money; }; exports.sample = testMethod;
Assign testMethod to the variable named exports.sample. This makes the testMethod available to other modules under the name sample.
Let’s stub the return value of this testMethod with sinon.
Here is the index.test.js.
const chai = require('chai'); const sinon = require('sinon'); const assert = chai.assert; const expect = chai.expect; const target = require('../../main/sample001/index.js');// require describe('sinon', function () { it('sinonのテスト', function(){ let stub = sinon.stub(target,'sample');// Stub the target's sample let expected = 110; stub.returns(expected);// Try to set the return value of SAMPLE to 110. let ret = target.sample(50); console.log(ret); expect(expected).to.equal(ret); }); });
Since stub.returns specifies a normal return value (not Promise, etc.), you now have a stub.
If you then execute sample(), the return value will be the specified return value no matter what the argument is.
The result will be as follows
Stubbing methods within methods in sinon
In practice, I think there are many cases of stubbing a method that searches in the DB within a method, for example.
The following is an example of stubbing a method within a method.
function testMethod(money=0){ let self = this; money = money * self.getTax();// Get sales tax from another method return money; }; function getTax() { return 1.08;// Image retrieved from DB } module.exports = { sample: testMethod, getTax: getTax };
I think getTax would normally be retrieved from DB, but since this is a test to see if it can be stubbed, I decided to just return 1.08.
Since this is a test to see if it can be stubbed, we decided to just return 1.08. index.test.js stubs the 1.08 and returns 1.10. The test itself is run from the testMethod method. The test itself is executed from the testMethod method.
const chai = require('chai'); const sinon = require('sinon'); const assert = chai.assert; const expect = chai.expect; const target = require('../../main/sample001/index.js'); describe('sinon', function () { it('sinon test', function(){ let stub = sinon.stub(target,'getTax');// Stab. let val = 1.10; stub.returns(val);// The return value of the getTax method is 1.10 let ret = target.sample(100);// Input is 100 let expected = 110;// Expected value is 110 expect(expected).to.equal(parseInt(ret)); }); });
Stubbing properties for object keys in sandbox
Returns a sandbox object with the sinon.sandbox.create() method.
*sandbox.create() is deprecated.
Instead, use the require('sinon').createSandbox()
method.
sandbox.stub(object name,'key').values('Value to be stubbed')
This description allows you to stub properties for the keys of an object.
The following is an example of execution.
const chai = require('chai') const sandbox = require('sinon').createSandbox() const assert = chai.assert const expect = chai.expect describe('sinon', function () { it('sinon test', function(){ const myObject = { 'hello': 'world' } sandbox.stub(myObject, 'hello').value('Sinon'); // returns is also acceptable in place of value let expected = 'Sinon'; expect(expected).to.equal(myObject.hello); }) afterEach(() => { sandbox.restore() }) after(() => { }) });
The result is success.
Stubbing the AWS-SDK(CommonJS)
Stub a makeRequest of AWS.Service.prototype. Returns either reject or resolve.
The following is an example of stubbing the confirmSubscription method of SNS.
Test Code
const aws = require('aws-sdk') ~~ ~~ const stub = sandbox.stub(AWS.Service.prototype, 'makeRequest') stub.returns({ promise: () => { return Promise.resolve({'SubscriptionArn':'fuga'}) } })
code
const aws = require('aws-sdk') const sns = new aws.SNS({ apiVersion: '2010-03-31', region: 'ap-northeast-1' }) ~~ ~~ const ret = await sns .confirmSubscription({ TopicArn: '', Token: '' }).promise() // I can mock it up.
Resoleve or reject the return value of Promsie.all.
sandbox.stub(Promise. 'all').resolves() sandbox.stub(Promise, 'all').rejects()
Here is an example of stubbing the getObject method (static async function) of your own Utility module.
sandbox.stub(Utility, 'getObject').resolves({ "Body": '{ "intercomDeviceId": "00c08f445566" }' })
Error in sinon
Cannot stub non-existent own property XXX
…This error occurs when there is no method named XXX.
TypeError: Attempted to wrap execute which is already wrapped
…It occurs when you try to stub something that has already been stubbed again.
How to stub process.env (environment variable)
Use sandbox to stub process.env (environment variable).
The createSandbox method creates the sandbox and afterEach restores the sandbox.
Here is the latest documentation on stubs and sandbox.
const sandbox = require('sinon').createSandbox() //sandbox作成 afterEach(()=>{ sandbox.restore()// Restoring the sandbox }) after(()=>{ })
Stub environment variables.
sandbox.stub(process.env, 'PATH').value('12345');// Stub the PATH environment variable to 12345
The above will change the value of the PATH environment variable. However, if the environment variable is not set on the PC, the following error will occur
TypeError: Cannot stub non-existent own property PATH
Below is an example of execution.
const chai = require('chai') const sandbox = require('sinon').createSandbox() const expect = chai.expect describe('sinon', function () { it('sinon test', function(){ sandbox.stub(process.env, 'PATH').value('12345') expect(process.env.PATH).to.equal('12345') // pass }) afterEach(function () { sandbox.restore() // Restoring the sandbox }) after(function () { }) })
Stubbing objects
The sinon.stub method allows only objects to be specified in the first argument.
sinon.stub(target);
In this case, we will stub all methods that target has.
Suppose target has methods aaa,bbb,ccc. In that case, we need to restore() all methods as follows.
beforeEach(function (done) { stub = sinon.stub(target);// No method is specified in the second argument. done(); }); afterEach(function (done) { stub.aaa.restore(); stub.bbb.restore(); stub.ccc.restore(); }
Dynamically changing the return value of a method
You can stub the method, but you may want to change the first return value and the second return value.
Use the onCall method as follows
const stub = sinon.stub(target.service, 'getDb'); stub.onCall(0).returns(Promise.resolve('1'));// Return 1 the first time. stub.onCall(1).returns(Promise.resolve('2'));// Return 2 for the second time.
onCall(0) is the first return value.
onCall(1) is the second return value.
Difference between sandbox.stub().returns, resolves and yields
returns specifies a normal return value.
resolves and rejects specify Promise.
Use yields
or callArgsWith
for functions with callback functions.
SINON Assertions
The standard assertions are provided in SINON.
sinon.assert.called(index.tax) // Verification of being called sinon.assert.calledOnce(index.tax) // Verification of being called once sinon.assert.notCalled(index.tax) // Verification of not being called sinon.assert.calledTwice(index.tax) // Verification of being called twice sinon.assert.callOrder(index.tax, index.age) // Verification of the order in which stubbed methods are called sinon.assert.callCount(index.tax, 3) // Verification of the number of times a stubbed method has been called
This is a sandbox assertion, verifying how many times Promise.all is called.
sandbox.stub(Promise, 'all').resolves() // processing sandbox.assert.calledOnce(Promise.all)
calledOnce, calledTwice, calledThrice, etc. are available.
Return value memo
Example of return value of listObjectsV2 method
const listObjectsV2Ret = { IsTruncated: false, Contents: [ { Key: 'var/tmp/a.json', last_modified: '2020-07-07T09:40:57.000Z', etag: '"14758w1afd44c09b7456167wwa00b43d"', size: 24, storage_class: 'STANDARD' } ], Name: 'bucket', Prefix: 'var/tmp/', MaxKeys: 1000, KeyCount: 1 }
Mocking a class that is new
How to mock a class.
code
module.exports.Index = class Index { constructor() {} async _get() { // ~~ } }
Test Code
const Index = require('../src/index').Index const sandbox = require('sinon').createSandbox() describe('', async () => { it('_get method test',async () => { sandbox.stub(Index.prototype, '_get').callsFake(() => { return {} }) // ~~ }) })
コメント