I have a page developed with angular 1.5, it shows a loading progress while initial API call, I want to write test case for this. I'm using a variable this.loaded to handle the loading progress. Initially this.loaded = false and once everything is loaded it will be this.loaded =true, but when I write test case this.loaded is alway true since the test case is checking final value of the variable. How I can slow down the flow and check the progress is getting triggered or not using Jasmine test?
HTML:
<loading-directive is-loaded='$ctrl.loaded'>
DOM to show after loaded
</loading-directive>
JS:
function componentCtrl (someFactory) {
var _this = this;
_this.loaded = false;
someFactory.someMethod().then(function () {
_this.loaded = false;
});
}
I want to check the _this.loaded = false before the call happens.
You cannot check the value of _this.loaded before the factory call. A work-around that you could do to achive that is to mock the factory call in a way that it doesn't change the _this.loaded and you can still check it is false. I will create an example that might give you an insight. I hope it helps:
describe('MyController Spec', function() {
// Load module
beforeEach(angular.mock.module('myApp'));
it('Check if loaded is false if factory call does not change its value', inject(function($controller) {
//setup
spyOn(someFactory, 'someMethod').and.callFake(function() {
//create a promise mock that will not execute the code that
//changes _this.loaded
var uselessPromiseMock = {
then: function() {}
};
return uselessPromiseMock;
});
//action
var myController = $controller("componentCtrl");
//assert
expect(myController.loaded).toBeFalsy();
}));
it('Check if loaded is true after a normal controller initialization', inject(function($controller) {
//action
var myController = $controller("componentCtrl");
//assert
expect(myController.loaded).toBeTruthy();
}));
});
PS: I'm assuming you mistyped and the callback that you pass to the factory call changes _this.loaded to true :)
Related
I have some logic inside my promise. Is it possible to cover this logic with unit tests? For example, I fire google analytics event inside this promise in my controller and want to make something like expect($analytics.eventTrack).toHaveBeenCalledWith(...).
Finally, the way to cover the code which is inside the promise can be the following:
let's suppose that on saving some data we fire a GA event inside our promise which says that the data was saved.
We have a service VoteService which has a method saveVote. This method makes a request to the server and returns a promise. Inside our promise, we fire GA event (when save was successfully executed).
To write the test for this we need in beforeEach:
create a promise saveVoteDeferrer = $q.defer();
resolve the promise with needed data
spyOn service's method
spyOn(VoteServiceMock,'saveVote')
.and.returnValue(saveVoteDeferrer.promise);
And then in it:
Call controller's method which contains service's method:
$ctrl.addVote();
verify if the GA event was fired inside our promise body
expect($analytics.eventTrack)
.toHaveBeenCalledWith('NewVoteAdded', {});
Bellow the sample:
voteController.js
var vm = this;
vm.addVote = function(){
//some logic goes here
VoteService.saveVote(vote)
.then(function(result){
//some logic goes here
$analytics.eventTrack('NewVoteAdded', {});
});
}
VoteService.js
return function(){
this.saveVote = function($http){
// some logic goes here
return $http.post(/*needed parameters*/);
}
}
vote.spec.js
describe('some text', function(){
beforeEach(function () {
// inject what you need
// mock VoteService service
// inject voteController controller
var saveVoteDeferrer = $q.defer();
spyOn($analytics,'eventTrack');
saveVoteDeferrer.resolve({ data: { } });
spyOn(VoteServiceMock,'saveVote')
.and.returnValue(saveVoteDeferrer.promise);
it('fire GA event when the vote is saved', function(){
$ctrl.addVote();
expect($analytics.eventTrack)
.toHaveBeenCalledWith('NewVoteAdded', {}); });
});
});
I would like a function I am spying on not execute until some condition is true.
Here is an example:
function openInfoDialog(id) {
let scope = $scope.$new(true);
scope.dataLoading = true;
api.getData(id).then(data => {
let processedData = process(data);
scope.columns = processedData.columns;
scope.data = processedData.data;
scope.dataLoading = false;
});
ngDialog.open({
//various dialog params,
scope
});
}
In my test I am trying to verify how the data is returned by the process function. The only way I can see to check that is to spy on ngDialog.open and check what is was called with.
However in my test ngDialog is being called before the process function finishes, meaning scope.data is undefined.
Is there any way I can tell ngDialog to wait until the then block is complete in the test? Or wait until scope.dataLoading is true.
To be clear, I do not want to change the functionality in my controller, I need to test what is currently written above.
Thanks for any help.
Spy on api.getData and mimic a promise return which is sync.
it('should process data', function () {
var mockedDataObject = { ... };
spyOn(api, 'getData').and.returnValue({
then: function () { // Mimic a promise
return mockedDataObject;
}
});
openInfoDialog(123);
expect(api.getData).toHaveBeenCalledWith(123);
expect(scope.columns).toEqual(whateveryouexpect)
expect(scope.data).toEqual(whateveryouexpect)
expect(scope.dataLoading).toEqual(whateveryouexpect)
});
P.S. you mentioned you want to
verify how the data is returned by the process function
In order to do that appropriately, firstly bind the process function onto scope, so that it can be accessed from tests. Secondly, call that function directly and check what it returned. You are meant to test functionality in isolation.
it('should process data', function () {
var mockedDataObject = { ... };
var mockedProcessedData = scope.process(mockedDataObject);
expect(mockedProcessedData.columns).toEqual(expectedcolumns);
expect(mockedProcessedData.data).toEqual(expecteddata);
});
Trying to write a jasmine test for the below code...
refreshCacheIfNewVersionIsAvailable();
//Check if a new cache is available on page load and reload the page to refresh app cache to the newer version of files
function refreshCacheIfNewVersionIsAvailable() {
$window.addEventListener('load', function (e) {
$window.applicationCache.addEventListener('updateready', function (e) {
if ($window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Manifest changed. Now Browser downloadeds a new app cache.
alert(textService.versioning.newVersionMessage);
$window.location.reload(true);
} else {
// Manifest didn't change. Nothing new to server.
}
}, false);
}, false);
}
Your challenge
I assume the challenge you are facing is that you are unable to see how to test the code in the callback functions. You just have to realize that you have access to the callback function when you spy on addEventListener, after the spy is executed in your service under test (refreshCacheIfNewVersionIsAvailable). Since you can get a reference to it, you can execute it, just as if it was the function you were testing.
Sample solution
The following is untested, written off the top of my head, but something along the lines of what I would expect to write if I had to test that code.
describe('refreshCacheIfNewVersionIsAvailable()', function() {
beforeEach(function() {
spyOn($window, 'addEventListener');
});
it('should register a load event handler on the window', function() {
refreshCacheIfNewVersionIsAvailable();
expect($window.addEventListener.calls.count()).toBe(1);
var args = $window.addEventListener.calls.argsFor(0);
expect(args.length).toBe(3);
expect(args[0]).toBe('load');
expect(typeof args[1]).toBe('function');
expect(args[2]).toBe(false);
});
describe('load event', function() {
var loadFunction;
beforeEach(function() {
refreshCacheIfNewVersionIsAvailable();
var args = $window.addEventListener.calls.argsFor(0);
loadFunction = args[1];
spyOn($window.applicationCache, 'addEventListener');
});
it('should register an updateready event handler in the window application cache', function() {
loadFunction();
expect($window.applicationCache.addEventListener.calls.count()).toBe(1);
var args = $window.applicationCache.addEventListener.calls.argsFor(0);
expect(args.length).toBe(3);
expect(args[0]).toBe('updateReady');
expect(typeof args[1]).toBe('function');
expect(args[2]).toBe(false);
});
describe('updateready event', function() {
var updateReadyFunction;
beforeEach(function() {
loadFunction();
var args = $window.applicationCache.addEventListener.calls.argsFor(0);
updateReadyFunction = args[1];
});
it('should reload the window if the status is UPDATEREADY', function() {
// You get the point
});
});
});
});
How do you write tests for something like FabricJS in a directive and service?
Example app: http://fabricjs.com/kitchensink/
I have been trying but I'm not making much progress without really bad hacks.
I want to integrate this service and directive into my https://github.com/clouddueling/angular-common repo so others can use this powerful library.
My scenario:
I'm trying to test my module that contains a service and directive. Those link my app to FabricJS. I'm having issues mocking the global fabric var that is created when you include the js file. I'm assuming then I spy on the var containing the fabric canvas.
I just need to confirm that my service is interacting with fabric correctly. I'm having trouble mocking/stubbing fabric though.
To win the bounty:
Example of a test I could use with Karma.
It's difficult as you've not provided the code you want to test. However, for testability, I would firstly create a very small factory to return the global fabric object
app.factory('fabric', function($window) {
return $window.fabric;
});
This factory can then be tested by injecting a mock $window, and checking that its fabric property is returned.
describe('Factory: fabric', function () {
// load the service's module
beforeEach(module('plunker'));
var fabric;
var fakeFabric;
beforeEach(function() {
fakeFabric = {};
});
beforeEach(module(function($provide) {
$provide.value('$window', {
fabric: fakeFabric
});
}));
beforeEach(inject(function (_fabric_) {
fabric = _fabric_;
}));
it('should return $window.fabric', function () {
expect(fabric).toBe(fakeFabric);
});
});
An example service that then uses this factory is below.
app.service('MyFabricService', function(fabric) {
this.newCanvas = function(element) {
return new fabric.Canvas(element);
}
this.newRectangle = function(options) {
return new fabric.Rect(options);
}
this.addToCanvas = function(canvas, obj) {
return canvas.add(obj);
}
});
You can then test these methods as below. The functions that return 'new' objects can be tested by creating a mock fabric object with a manually created spy that will be called as a constructor, and then using instanceof and toHaveBeenCalledWith to check how its been constructed:
// Create mock fabric object
beforeEach(function() {
mockFabric = {
Canvas: jasmine.createSpy()
}
});
// Pass it to DI system
beforeEach(module(function($provide) {
$provide.value('fabric', mockFabric);
}));
// Fetch MyFabricService
beforeEach(inject(function (_MyFabricService_) {
MyFabricService = _MyFabricService_;
}));
it('should return an instance of fabric.Canvas', function () {
var newCanvas = MyFabricService.newCanvas();
expect(newCanvas instanceof mockFabric.Canvas).toBe(true);
});
it('should pass the element to the constructor', function () {
var element = {};
var newCanvas = MyFabricService.newCanvas(element);
expect(mockFabric.Canvas).toHaveBeenCalledWith(element);
});
The addToCanvas function can be tested by creating a mock canvas object with an 'add' spy.
var canvas;
// Create mock canvas object
beforeEach(function() {
canvas = {
add: jasmine.createSpy()
}
});
// Fetch MyFabricService
beforeEach(inject(function (_MyFabricService_) {
MyFabricService = _MyFabricService_;
}));
it('should call canvas.add(obj)', function () {
var obj = {};
MyFabricService.addToCanvas(canvas, obj);
expect(canvas.add).toHaveBeenCalledWith(obj);
});
This can all be seen in action in this Plunker http://plnkr.co/edit/CTlTmtTLYPwemZassYF0?p=preview
Why would you write tests for an external dependency?
You start by assuming FabricJS just works. It's not your job to test it, and even if it were, you'd have to do byte stream comparison (that's what a canvas is, a stream of bytes interpreted as an image). Testing user input is a whole different thing. Look up Selenium.
Then you write tests for the code that produces the correct input for FabricJS.
I'm working with backbone and jasmine and now
trying to test the callCount of 'sync' method, when model saved.
For some strange reason the sync handler continue to handle the sync even after the done variable is true (this is there i planned to stop the test)
I'm a newbie for jasmine so i guess i didn't understand something elementar here...
here is my speck:
describe('Model :: User', function() {
var mockData = { name: 'Foo Bar' };
beforeEach(function() {
var that = this,
done = false;
require(['app/namespace','app/models/UserModel','app/collections/UsersCollection'], function(namespace, UserModel ,UsersCollection) {
that.users = new UsersCollection();
that.user = new UserModel();
done = true;
});
waitsFor(function() {
return done;
}, "Create Models");
});
afterEach(function(){
var done = false,
isDone = function(){ return done; };
this.users.fetch({
success: function(c) {
console.log('after the test calling destory of collection...')
c.each(function(m){
m.destroy();
});
done = true;
}
});
waitsFor(isDone);
done = false;
this.user.destroy({
success: function(){
console.log('after the test calling destory of model...')
done = true;
}
});
waitsFor(isDone);
});
describe('.save()', function() {
it('should call sync when saving', function() {
var done = false,
spy = jasmine.createSpy();
this.user.on('sync', spy);
this.user.on('sync', function(){
console.log('checking spy.callCount-'+spy.callCount);
//------------------------------------------------
if(!done)//why i need this if ?!
expect(spy.callCount).toEqual(1);
done = true;
}, this);
this.user.save(mockData);
waitsFor(function() { return done; });
});
});
});
The test workiing correctly only if i add "if(!done)" condition before expect statement,
otherwise it continue to count sync calls that caused by destroy after the test...
Thanks forwards
There are some of issues with this test. First of all, you dont need to test that the sync event is fired when saving your model cause this is provided by another framework, which is hopefully tested.
Second you should use the fake server of SinonJs to not mess with async calls. With sinon your request will be called immediately, which means you dont need waitsFor. Also assertions in callback seems a bit odd.
this.server = sinon.fakeServer.create();
server.respondWith({data: 'someData'})
server.autoRespond = true; //so when the request start the fake server will immediately call the success callback
var spy = jasmine.createSpy();
this.user.on('sync', spy);
this.user.save(mockData);
expect(spy.callCount).toEqual(1);