Sinon stub pass through arguments - javascript

I want to be able to just return the arguments that a stub receives passed to a promise, is this possible:
Impl:
function(arg) {
return db.find(arg)
.then(result => {
return fnIWantToStub(result);
})
.then(nextResult => {
return done()
});
Test:
var stub = sinon.stub(fnIWantToStub);
stub.returns(PassedThruArgsThatReturnsAPromise);
Can this be done?

The docs state that you can return an arg at a given index.
stub.returnsArg(index);
I don't think there is a blanket return all args.
(UPDATE after question edit)
You could stub the db.find function and resolve it. I use mocha and chai.
const dbStub = sinon.stub(db, 'find);
const expected = {..};
dbStub.returns(Promise.resolve(someDataFixture));
const result = functionUnderTest(args);
// using chaiAsPromised
return expect(result).to.eventually.equal(expected);
Note you have to return to get Mocha to run through the promise. You can also use mocha's done callback. Although quite often I'll just use a Promise.
Checkout http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

Related

jest.mock function not being used after a mockRestore is called on a different mocked function

I am trying to mock the AWS SDK. I have a function, ingest that does some logic (including some calls to the AWS SDK sqs.deleteMessage function) and then calls ingestAndSave (which also makes some calls to the sqs.deleteMessage function).
When testing the ingest function, I want to mock my ingestAndSave function so I can have those tests focus purely on the ingest function logic.
Then I want to test the ingestAndSave function logic in its own describe block - and test the real thing, not the mocked version.
When testing either ingest or ingestAndSave functions I need to use my mocked sqs.deleteMessage
My most recent attempt includes following the path here: https://www.lucasamos.dev/articles/jestmocking. My test suite looks like this:
const ingest = require('./ingest')
const TEST_CONSTANTS = require('../../../tests/constants')
const mockdeleteMessage = jest.fn().mockImplementation( ()=> {
console.log("MOCK DELETE MESSAGE FUNCTION");
return {}
} )
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {}
}
},
SQS: jest.fn(() => {
return {
deleteMessage: jest.fn(( { params } )=> {
return {
promise: () => mockdeleteMessage()
}
}),
}
})
}
});
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
test("ingestAndSave will be called two times when event has two Records", async () => {
await ingest.ingest(TEST_CONSTANTS.sqsIngestEventMessageWithTwoRecords)
expect(ingestAndSaveFunctionSpy).toHaveBeenCalledTimes(2)
})
afterEach(() => {
jest.resetAllMocks()
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("FUNCTION: ingestAndSave", () => {
let validParams = {camera: TEST_CONSTANTS.validCameraObject, sourceQueueURL:"httpexamplecom", receiptHandle: "1234abcd"}
test("Will call deleteMessage once when given valid paramters", async () => {
await ingest.ingestAndSave(validParams)
expect(mockdeleteMessage.mock.calls.length).toBe(1)
})
/** snip other tests */
})
Both of the above functions run through code that looks just like this:
let deleteResp;
try {
deleteResp = await sqs.deleteMessage({ QueueUrl: sourceQueueURL, ReceiptHandle: receiptHandle}).promise()
} catch (err) {
console.error('Task: Deleting message from the ingest queue: ', err, 'deleteResp: ', deleteResp, 'receiptHandle: ', receiptHandle)
}
The mocked sqs.deleteMessage is used for the first describe block (i.e. I see the console.log for it) and the test passes.
However, the mocked sqs.deleteMessage function is not used for the second describe block (i.e. I do not see the console.log message indicating the mocked function ran, and, in fact, I get a 5000ms timeout, indicating the real sqs.deleteMessage was called (with invalid authorization, the deleteMessage command takes >5 seconds).
I thought the jest.restoreAllMocks() in the afterAll block of the first describe is restoring my mock. So I go with explicitly restoring the ingestAndSaveFunctionSpy mock with ingestAndSaveFunctionSpy.mockRestore() instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the first describe block, but it has been restored by the ingestAndSaveFunctionSpy.mockRestore() call.
Just to test, I remove the afterAll in the first describe entirely. This results in the second test calling the mocked implementation of ingestAndSave and thus the test failing.
Declare the jest.mock... within each describe block, but this isn't allowed due to jest.mock calls getting hoisted to the top of the file.
How can I mock a module using jest.mock(... and have it persist between describe blocks while allowing mockRestore() calls to other mocked functions?
How to change mock implementation on a per single test basis? has me looking at mockImplemention but I'm struggling to see how I'd implement it.
See related question attempting to tackle this from a different angle: How to have jest.mock persist between tests?
If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
// ...
afterEach(() => {
ingestAndSaveFunctionSpy.mockRestore()
})
})
In the outer scope, I would also advise using a:
afterEach(() => {
jest.clearAllMocks()
})
This will reset the recordings of the mock calls to your aws-sdk mock without actually changing any of the mocked behavior. Otherwise, you might be verifying behavior in one test that actually happened during the execution of another.
Without seeing the code, it's hard to offer more specific advice, but this style of test is a code smell, IMO. Any time you have to spy on your own unit in a unit test, it's usually a sign of poor organization. Try to arrange it so that discreet units of work are discreet functions. As an abstract example, instead of chaining functions together like:
const a = (arg) => {
const stuff = /* do some stuff with arg */
b(stuff);
};
const b = (arg) => {
const stuff = /* do some stuff with arg */
c(stuff);
}
const c = (arg) => {
/* do some stuff with arg */
}
Instead make them discreet, testable units that are joined together in the external function:
const abc = (arg) => {
const firstStuff = a(arg);
const secondStuff = b(firstStuff);
c(secondStuff);
}
const a = (arg) => {
return /* do some stuff with arg */
}
const b = (arg) => {
return /* do some stuff with arg */
}
const c = (arg) => {
/* do some stuff with arg */
}
Now you can write tests for a, b, and c independently without having to mock your own internals.
I solved this by wrapping all calls to aws-skd functions in my own functions and then mocking my wrapper functions.
So now I have a utility functions in utils.js that looks like this:
module.exports.sendMessage = async (params) => {
return await sqs.sendMessage(params).promise()
}
And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):
await utils.sendMessage(sendMessageParams).catch(err => throw (err))
And then during testing, I mock it like this - either in a beforeEach or directly before my test:
sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true}))
Later, rinse, and repeat for all aws-sdk functions I want to mock.

Jest: how to count call from mock methods called via `call` or `apply`?

How can I use mocks to count function calls made via call or apply
// mylib.js
module.exports = {
requestInfo: function(model, id) {
return `The information for ${model} with ID ${id} is foobar`;
},
execute: function(name) {
return this[name] && this[name].apply(this, [].slice.call(arguments, 1));
},
};
// mylib.test.js
jest.mock('./mylib.js');
var myLib = require('./mylib.js');
test('', () => {
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // Success!
expect(myLib.requestInfo.mock.calls.length).toBe(1); // FAIL
});
If I explicitly call myLib.requestInfo, the second expectation succeeds.
Is there a way to watch module mock calls whose functions were called via apply or call?
From the jest.mock doc:
Mocks a module with an auto-mocked version when it is being required.
The docs could probably be improved with a better description of what "auto-mocked version" means, but what happens is that Jest keeps the API surface of the module the same while replacing the implementation with empty mock functions.
So in this case execute is getting called but it has been replaced by an empty mock function so requestInfo never gets called which causes the test to fail.
To keep the implementation of execute intact you will want to avoid auto-mocking the entire module and instead spy on the original function with something like jest.spyOn:
var myLib = require('./mylib.js');
test('', () => {
jest.spyOn(myLib, 'execute'); // spy on execute
jest.spyOn(myLib, 'requestInfo') // spy on requestInfo...
.mockImplementation(() => {}); // ...and optionally replace the implementation
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // SUCCESS
expect(myLib.requestInfo.mock.calls.length).toBe(1); // SUCCESS
});

Why does Typescript think async/await returns a value wrapped in a promise?

I want to refactor a promise chain into async/await, but Typescript is complaining about the typing.
TS2322:Type 'IHttpPromiseCallbackArg< IResp >' is not assignable to type 'IResp'...
I thought await would return a regular value, not a promise. Am I wrong? If so, how can I assign a typing so that the desired code will compile?
I thought await would return the same value as the first argument in the .then callback. Am I wrong?
Old code:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Desired new code:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
The desired code still breaks if I remove the return type in myAsyncRequest ; I guess Typescript infers directly from the AngularJS library.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
If I remove "IResp" from the const resp declaration, processResponse complains that IHttp< IResp> does not equal IResp...
processResp(resp:IResp) {
//do stuff
}
Your question "I thought await would return the same value as the first argument in the .then callback. Am I wrong?".
No, you are absolutely right. But you are wrong about what the first argument in the .then callback is.
You define myAsyncRequest to return IHttpPromise<IResp>. But IHttpPromise<T> is defined as inheriting IPromise the following way:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
So, an IHttpPromise<T> is a promise that returns an IHttpPromiseCallbackArg<T> back where the actual data of type T is in the data property of the IHttpPromiseCallbackArg<T>.
So, the old code variant we see in the question:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
should actually not compile without errors in TypeScript when myAsyncRequest is defined to return IHttpPromise.
Howto fix this:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Note: In the latest type definitions for angular, the type IHttpPromiseCallbackArg<T> is actually called IHttpResponse<T>.
Maybe in your code, you have defined IResp as IHttpResponse<Something>? Then you just have a conflict with the old name IHttpPromiseCallbackArg. Then get the newest type definitions from DefinitelyTyped that uses the new name. And you would also have to change the definition of myAsyncRequest to:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
The line containing the await does indeed await the resolved value - but because the function itself is async (to allow all that awaiting), you get back a promise.
Example... in the below code you can use x as a plain number (even though delay returns a promise) and again for y - so all the stuff you await is resolved so you can use it more like you would if it were synchronous.
The async function that doesn't look like it returns a promise, now does.
This can seem confusing, because it seems to "invert the promises", but what it does is shift the then to the top level (you could have async functions calling down to other async functions and so on).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));

Return value from nested promise with Webdriver

In my test code I wish to achieve the following:
it('Updates label text', function(done) {
page.testLabelText();
assert.equal(page.testLabelText().pageLabel, page.testLabelText().iFrameLabel);
done();
});
In my page object, here is testLabelText();:
page.testLabelText = function () {
var pageLabel = function () {
return driver.findElement(By.css('#element')).getText().then(function(text) {
return text;
});
};
var iFrameLabel = function () {
return driver.findElement(By.css('#element')).getText().then(function(text) {
return text;
});
};
return {
pageLabel: pageLabel(),
iFrameLabel: iFrameLabel()
};
};
But this returns 'Undefined' when printed to the console...I'm a newbie at javascript so though I've managed this in regular javascript, everything I've tried has failed with selenium WebdriverJS promises...
Your assert.equal() is comparing two distinct promise objects so that will never be true. To understand why, here's the step by step. What you need to do is to get the values out of both the promises after they are resolved and then compare the values.
page.testLabelText(); by itself just returns an object so calling it by itself with no assignment of the return value or referencing the return value does nothing.
page.testLabelText().pageLabel by itself is a promise.
page.testLabelText().iFrameLabel by itself is a promise.
And, they are different promise objects so your assert.equal() will not be true.
If you wanted to compare the two values from the promises, you'd have to do something like this:
var obj = page.testLabelText();
Promise.all(obj.pageLabel, obj.iFrameLabel).then(function(results) {
assert.equal(results[0], results[1]);
done();
});
The solution was to use an assertion library that could resolve promises in the tests, as this would be impossible using regular async asserts. In this case I used Chai as Promised.
Requiring the following:
chai = require('chai'),
chaiAsPromised = require("chai-as-promised"),
should = chai.should();
and including chai.use(chaiAsPromised); in mocha's before hook, I could then write
it('Updates label text', function() {
var label = FormsPage.testLabelText();
label.labelHeading.should.eventually.contain(label.userInput);
});
I found a blog post on this here

Asserting function calls inside a promise

I'm writing some tests for an async node.js function which returns a promise using the Mocha, Chai and Sinon libraries.
Let's say this is my function:
function foo(params) {
return (
mkdir(params)
.then(dir => writeFile(dir))
)
}
mkdir & writeFile are both async functions which return promises.
I need to test that mkdir is being called once with the params given to foo.
How can I do this?
I've seen quite a few examples on how to assert the overall return value of foo (sinon-as-promised is super helpful for that) but non about how to make sure individual functions are being called inside the promise.
Maybe I'm overlooking something and this is not the right way to go?
mkdir isn't called asynchronously here, so it's rather trivial to test:
mkdir = sinon.stub().resolves("test/dir")
foo(testparams)
assert(mkdir.calledOnce)
assert(mkdir.calledWith(testparams))
…
If you want to test that writeFile was called, that's only slightly more complicated - we have to wait for the promise returned by foo before asserting:
… // mdir like above
writeFile = sinon.stub().resolves("result")
return foo(testparams).then(r => {
assert.strictEqual(r, "result")
assert(writeFile.calledOnce)
assert(writeFile.calledWith("test/dir"))
…
})
You can mock the mkdir function then use setTimeout to check whether or not this function is called.
describe('foo', () => {
it('should call mkdir', () => {
return new Promise((resolve, reject) => {
let originalFunction = mkdir;
let timer = setTimeout(() => {
reject(`mkdir has not been called`);
});
mkdir = (...params) => new Promise(mkdirResolve => {
//restore the original method as soon as it is invoked
mkdir = originalMethod;
clearTimeout(timer);
mkdirResolve(someExpectedData);
resolve();
});
foo(params);
});
});
});

Categories