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.
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'm new to Angular and I'm still trying to figure out how things work for it. I'm currently having trouble testing a Component that depends on a Service that returns a Promise. The function I'm testing is structured like the following:
success: boolean;
borrowBook() {
this.bookService.borrow(this.selectedBook.id)
.then(() => {
this.success = true;
})
.catch((error: BorrowingError) => {
this.success = false;
});
}
Now, I'm not really sure if something like the code above is considered idiomatic, but that's how I wrote the code. In my unit test, I mocked the bookService using jasmine.createSpyObj function, and defined the stub as follows:
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
However, my test fails saying that component.success is undefined when I expected it to be truthy. My test is programmed as follows:
it('test', async(() => {
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
//setup pre-conditions here...
component.borrowBook(();
expect(component.success).toBeTruthy();
}));
My impression is that the expectations are being checked even before the Promise is handled accordingly.
I chanced upon this article about testing asynchronous code in Angular, which stated that the flushMicrotasks function can be used to run all the async components before checking expectations. This is only provided through the fake zone created through the fakeAsync, so in my code I used that instead of just async.
it('test', fakeAsync(() => { //used fakeAsync instead
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
//setup pre-conditions here...
component.borrowBook(();
flushMicrotasks(); //added this before checking expectations
expect(component.success).toBeTruthy();
}));
This question already has answers here:
Testing with React's Jest and Enzyme when simulated clicks call a function that calls a promise
(2 answers)
Closed 4 years ago.
I am testing a component that uses an external Api.js file. When testing the component, I'm mocking the function using
import {
apiCall,
} from './Api';
jest.mock('./Api');
I want to test a case where the Api call fails, to check that my component shows the correct error. In order to do that, I'm faking the Api response to be:
apiCall.mockImplementation(
() => Promise.reject({ error: 'This is my error' }),
);
However, when mocking the response in this way, I am not able to make it execute before the test case is evaluated. Is there any way to wait for that response to happen before finishing the test?
I have created a codesandbox as simple as I can showing the issue. Since seems that codesandbox does not allow to use jest.mock, I'm just making the original API call to return Promise.reject.
https://codesandbox.io/s/6y0rn74mzw
The behaviour is simple: A text "No error" and a button, that when clicked it calls to the API method that automatically returns Promise.reject. In the response, we change the text to "There is an error". The first test just looks for the word 'error' to make the test pass and show the full evaluation of the code (the test stops if something fails), and the second test is the test I would expect to pass if the correct order was applied.
What would be the way to assure the proper execution order in the test case?
When it comes to dealing with Promises in jest tests, what I've done is as follows:
it('test script description', (done) => {
apiCall()
.then((response) => {
// Verify the response from the promise
done();
});
});
I'm obviously not saying that this is the best solution (perhaps someone can point you in a better direction) but it's the only one that worked for me!
So the solution is to use setImmediate.
If I change the test case to be:
it("should pass but fails due to execution order", done => {
console.log("-----------");
expect(true).toBe(true);
const wrapper = mount(<App />);
wrapper.find("button").simulate("click");
setImmediate(() => {
const newWrapper = wrapper.update();
expect(newWrapper.html()).toContain("There is an error");
done();
});
console.log("Third");
console.log("-----------");
});
It passes.
A good explanation here: https://github.com/kentcdodds/react-testing-library/issues/11
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.
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.