I need to execute an argument that is a callback of a jest mock.
In the jest documentation, their callback example is about testing the first callback. I have behaviors inside nested callbacks I need to test. In the examples of promises, they use resolves and rejects. Is there anything like this for nested callbacks? I'm currently executing the mocked call argument, which I'm not sure if it is the recommended way.
System under test:
function execute() {
globals.request.post({}, (err, body) => {
// other testable behaviors here.
globals.doSomething(body);
// more testable behaviors here. that may include more calls to request.post()
});
}
The test:
globals.request.post = jest.fn();
globals.doSomething = jest.fn();
execute();
// Is this the right way to execute the argument?
globals.request.post.mock.calls[0][1](null, bodyToAssertAgainst);
expect(globals.doSomething.mock.calls[0][1]).toBe(bodyToAssertAgainst);
My question is in the comments in the code above. Is this the recommended way to execute a callback, which is an argument of the mocked function?
Since you don't care about the implementation of your globals.request.post method you need to extend your mock a bit in order for your test to work.
const bodyToAssertAgainst = {};
globals.request.post = jest.fn().mockImplementation((obj, cb) => {
cb(null, bodyToAssertAgainst);
});
Then you can go onto expect that doSomething was called with bodyToAssertAgainst. Also, this way you can easily test if your post would throw an Error.
Related
I've written a lot of asynchronous unit tests lately, using a combination of Angular's fakeAsync, returning Promises from async test body functions, the Jasmine done callback, etc. Generally I've been able to make everything work in a totally deterministic way.
A few parts of my code interact in deeply-tangled ways with 3rd party libraries that are very complex and difficult to mock out. I can't figure out a way to hook an event or generate a Promise that's guaranteed to resolve after this library is finished doing background work, so at the moment my test is stuck using setTimeout:
class MyService {
public async init() {
// Assume library interaction is a lot more complicated to replace with a mock than this would be
this.libraryObject.onError.addEventListener(err => {
this.bannerService.open("Load failed!" + err);
});
// Makes some network calls, etc, that I have no control over
this.libraryObject.loadData();
}
}
it("shows a banner on network error", async done => {
setupLibraryForFailure();
await instance.init();
setTimeout(() => {
expect(banner.open).toHaveBeenCalled();
done();
}, 500); // 500ms is generally enough... on my machine, probably
});
This makes me nervous, especially the magic number in the setTimeout. It also scales poorly, as I'm sure 500ms is far longer than any of my other tests take to complete.
What I think I'd like to do, is be able to tell Jasmine to poll the banner.open spy until it's called, or until the test timeout elapses and the test fails. Then, the test should notice as soon as the error handler is triggered and complete. Is there a better approach, or is this a good idea? Is it a built-in pattern somewhere that I'm not seeing?
I think you can take advantage of callFake, basically calling another function once this function is called.
Something like this:
it("shows a banner on network error", async done => {
setupLibraryForFailure();
// maybe you have already spied on banner open so you have to assign the previous
// spy to a variable and use that variable for the callFake
spyOn(banner, 'open').and.callFake((arg: string) => {
expect(banner.open).toHaveBeenCalled(); // maybe not needed because we are already doing callFake
done(); // call done to let Jasmine know you're done
});
await instance.init();
});
We are setting a spy on banner.open and when it is called, it will call the callback function using the callFake and we call done inside of this callback letting Jasmine know we are done with our assertions.
I have a class which contains methods.Now after initializing the class i want to invoke the methods but my test flow is not to the method and getting error like Uncaught error outside test suite.Below is my code
describe('Test Stu positive', () => {
it("Test Stu positive", (done) => {
const stuDetails = new masterDetails(getdetails);
expect(stuDetails.retrieveStudent(res => {
console.log(res);
done()
}))
});
});
Now, in the above code i am not able to print console.log(res);. What i am doing wrong?
I believe that you are using Mocha as your testing framework and looks like the error is not being handled by mocha, because is an asynchronous operation and you are not passing the error to the done callback method as described in the mocha documentation
it is really hard to tell how your function works, if it returns a promise or if it just uses a callback and the error is handled inside the function, so I can't provide you a code example on how to accomplish this. if you mind providing your function declaration I can update my answer with an example solution.
I am reading some tutorials on promise tests in mocha. There is a piece of codes:
before(function(done) {
return Promise.resolve(save(article)).then(function() {
done();
});
});
Why done() called in the then() in the before()? What is the difference between the above codes and the following codes:
before(function(done) {
return Promise.resolve(save(article));
});
Thanks
UPDATE
My question is to compare with the following codes:
before(function() {
return Promise.resolve(save(article));
});
Sorry for the typo.
The first code snippet with the before hook returns a promise and calls done. In Mocha 3.x and over, it will result in this error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
It used to be that it did not particularly matter if you used done and returned a promise, but eventually the Mocha devs figured that specifying both done and returning a promise just meant the test designer made a mistake and it was better to have Mocha pitch a fit rather than silently allow it.
In your 2nd snippet, you have the done argument and return a promise but Mocha will still wait for done to be called and will timeout. (It really should detect the argument and raise an error like in the 1st case, but it doesn't...)
Generally, if you are testing an asynchronous operation that produces a promise, it is simpler to return the promise than use done. Here's an example illustrating the problem:
const assert = require("assert");
// This will result in a test timeout rather than give a nice error
// message.
it("something not tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
});
});
// Same test as before, but fixed to give you a good error message
// about expecting a value of 2. But look at the code you have to
// write to get Mocha to give you a nice error message.
it("something tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
}).catch(done);
});
// If you just return the promise, you can avoid having to pepper your
// code with catch closes and calls to done.
it("something tested properly but much simpler", () => {
return Promise.resolve(1).then((x) => {
assert.equal(x, 2);
});
});
With regards to the completion of asynchronous operations, it works the same whether you are using it, before, beforeEach, after or afterEach so even though the example I gave is with it, the same applies to all the hooks.
I am not sure if I understood 100% the question, but the tests will not start until done is called.
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
This test will not start until the done function is called in the call to beforeEach above. And this spec will not complete until its done is called.
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
You don't have to pass done in your example, just:
before(function() {
return Promise.resolve(save(article));
});
If you do pass done the test runner will expect to be called before continue, otherwise it will probably throw a timeout error.
In this particular case there is no functional difference. The callback, often called done, was introduced to handle asynchronous tests when using callbacks. Returning a Promise is sufficient, but note that you cannot define the done callback, because the test suite will wait until it's called. Use done when you can't easily return a Promise. In your case the second test will be infinite, because you define done, which you never actually call.
So basically I have the function I want to test which we will call function A. I want to test if function B was called inside function A. The problem is that function B is being called inside function A asynchronously through a resolved Promise. This will cause sinon assertion to fail because the test will finish before function B is called!
Here is a working code scenario.
const sinon = require('sinon');
describe('functionToBeTested', () => {
it('someFunction is called', () => {
// spy on the function we need to check if called
const spy = sinon.spy(someClass.prototype, 'someFunction');
// call the function being tested
functionToBeTested();
// check if spy was called
sinon.assert.called(spy);
});
});
class someClass {
someFunction() {
console.log('Hello');
}
}
const somePromise = Promise.resolve();
function functionToBeTested() {
const instance = new someClass();
// some synchronous code here
// if instance.someFunction() is called here the test will pass
// .
// .
// .
somePromise.then(() => {
instance.someFunction();
// the function is called and Hello is printed but the test will fail
})
// .
// .
// .
// some synchronous code here
// if instance.someFunction() is called here the test will pass
}
Your example is a bit unconventional. You have functionToBeTested which has a dual behavior (sync and async at the same time). When you put this method under test, the behavior should be well known and standardized before hand, so that you can structure the test and assertions accordingly.
The problem in this scenario is that you try to validate the behavior of the function is sync mode, although the inner parts work in a fire-and-forget fashion - i.e. there is no dependency on the result of the instance.someFunction() method.
If functionToBeTested() returned a promise - thus being async by design, that would be straightforward for your test scenario. But in this case, you will need an unconventional testing approach as well. That means that if you do something like:
describe('functionToBeTested', () => {
it('someFunction is called', (done) => {
// spy on the function we need to check if called
const spy = sinon.spy(SomeClass.prototype, 'someFunction');
// call the function being tested
functionToBeTested();
setTimeout(() => {
// check if spy was called
sinon.assert.called(spy);
done();
}, 10);
});
});
the test would pass. What happened here is that we declared the test async by making use of the done argument in the callback. Also, we added a timer to mimic a delay prior to checking whether the spy was called.
Since the 'fire-and-forget' call only prints out a message, waiting for 10ms would be enough. If the promise took longer to complete, the wait time should be adjusted.
As stated before, unconventional implementations require unconventional approaches. I would suggest that you reconsider your requirements and redesign the solution.
How do asynchronous tests work in Intern testing framework? I have tried to get them run exactly as in the example, but the async test passes immediately without waiting for the callback to be run.
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.callback(function(result) {
expect(result).to.... something
}));
}
The test passes immediately. What am I doing wrong?
dfd.callback doesn’t execute anything until it itself is executed. Keep in mind that it is designed for promise callbacks (i.e. the function passed to promise.then), not Node.js-style callbacks where the argument might be an error (i.e. function (error, result) {}). It will not check to see if an error is passed as an argument.
Without knowing what conn is, but seeing how you are passing dfd.callback as an argument to something that is not a promise, my suspicion is you are trying to use a Node.js-style callback and the call is erroring immediately. We may provide a convenience wrapper for these types of callbacks in the future to convert them to a promise interface, but until then, you probably just need to do something like this:
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.callback(function(error, result) {
if (error) {
throw error;
}
expect(result).to.... something
}));
});
Otherwise, without knowing what conn is and seeing what your actual assertion is, it’s too hard to say what the issue is here. So long as nothing inside the callback throws an error, the test will be considered successful.
Edit: So based on your comments above it sounds like your callback is an event listener called multiple times with different information. In this case, what you could do is something like this:
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.rejectOnError(function (result) {
if (result !== 'what I want') {
return;
}
expect(result).to.... something
// all other tests…
// nothing threw an error, so it is a successful test
dfd.resolve();
}));
});
dfd.rejectOnError works just like dfd.callback except it does not automatically resolve the promise; you do that yourself at the end.
Your structure is okay. dfd sets a timeout of 5 seconds for the test to succeed, then immediately tries conn.connect(), which is not part of the intern framework. If you are trying a simple XHR request, try the getUrlCallback function instead.
They have a pretty cool list of tests at this url: https://github.com/theintern/intern/wiki/Writing-Tests . Look for the two async examples.