Here is my controller function I want to test.
saveItem = (): void => {
this.updateItem();
this.loadingDialogService.showIndicator("Saving Item");
this._editItemService.updateItem(this.item).then((updatedItem: Item) => {
this.loadingDialogService.cancelDialog();
this.goToMainView();
}).catch(() => {
this.loadingDialogService.showErrorDialog("Failed to Save Item");
//this._log.error("Error CallingItemService");
});
}
Here is my test:
it("should call method saveItem", () => {
spyOn(controller, 'updateItem');
spyOn(loadingDialogService, 'showIndicator');
spyOn(editItemService, 'updateItem').and.callFake(() => {
let result: Item
deferred.resolve(result);
return deferred.promise;
});
spyOn(loadingDialogService, 'cancelDialog');
spyOn(controller, 'goToMainView');
controller.saveItem();
expect(controller.updateItem).toHaveBeenCalled();
expect(loadingDialogService.showIndicator).toHaveBeenCalled();
expect(_editItemService.updateItem).toHaveBeenCalled();
expect(loadingDialogService.cancelDialog).toHaveBeenCalled();
expect(controller.goToMainView).toHaveBeenCalled();
});
The test fails at the last two expects, throws error saying that
Expected spy cancelDialog to have been called.
Expected spy goToMainView to have been called.
I guess the test doesn't execute the functions inside then function. Could some one point out where the mistake is?
With Jasmine async testing, we have to call the async code in the beforeEach() function that runs before each it() function block within a describe() function block. We also have to let Jasmine know when the async function has completed by calling the special done() callback function Jasmine provides.
Following are the steps to call a test case. Step 1 − Go to the Test Step section, where you want to call a test case. Step 2 − Go to individual step where the user will call the function. Step 3 − In the Settings tab of the Test step, click the Test Case icon as shown in the following screenshot.
If the function passed to Jasmine takes an argument (traditionally called done ), Jasmine will pass a function to be invoked when asynchronous work has been completed.
fakeAsync and tick Like async we wrap the test spec function in a function called fakeAsync . We call tick() when there are pending asynchronous activities we want to complete. Like the async function the fakeAsync function executes the code inside its body in a special fake async test zone.
You have a promise to resolve, so you need to run a digest cycle after your function call but before your tests.
it("should call method saveItem", () => {
spyOn(controller, 'updateItem');
spyOn(loadingDialogService, 'showIndicator');
spyOn(editItemService, 'updateItem').and.callFake(() => {
let result: Item
deferred.resolve(result);
return deferred.promise;
});
spyOn(loadingDialogService, 'cancelDialog');
spyOn(controller, 'goToMainView');
controller.saveItem();
$scope.$digest();
expect(controller.updateItem).toHaveBeenCalled();
expect(loadingDialogService.showIndicator).toHaveBeenCalled();
expect(_editItemService.updateItem).toHaveBeenCalled();
expect(loadingDialogService.cancelDialog).toHaveBeenCalled();
expect(controller.goToMainView).toHaveBeenCalled();
});
Having said that, your test is going to lead you to problems later because it has 5 assertions (expect()s) in it. When one fails you will have to waste time figuring out which one it is. Stick to One Assertion Per Test (OAPT.) This should be 5 tests with one assertion each. That way, when something fails you know right what it is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With