Looking at an example from Mastering Web Applications in AngularJS:
angular.module('archive', [])
.factory('notificationsArchive', function () {
var archivedNotifications = [];
return {
archive:function (notification) {
archivedNotifications.push(notification);
},
getArchived:function () {
return archivedNotifications;
}};
});
Then, the module's test:
describe('notifications archive tests', function () {
var notificationsArchive;
beforeEach(module('archive'));
beforeEach(inject(function (_notificationsArchive_) {
notificationsArchive = _notificationsArchive_;
}));
it('should give access to the archived items', function () {
var notification = {msg: 'Old message.'};
notificationsArchive.archive(notification);
expect(notificationsArchive.getArchived())
.toContain(notification);
});
});
What's going on in the second beforeEach(inject ...?
beforeEach(inject(function (_notificationsArchive_) {
notificationsArchive = _notificationsArchive_;
}));
That's just saying before each test, get an instance of notificationsArchive. It then assigns that instance to a variable that can be used in the actual test case. The underscores around notificationsArchive are just syntactic sugar so you don't have to come up with another name for the variable that your test users.
It is injecting the notificationsArchive service into a function that assigns that service to the local variable "notificationsArchive" before each test. The underscores in the name are ignored.
https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
Related
This is how my controller looks like:
(function() {
'use strict';
angular.module('myApp', [])
.controller('vmCtrl', vmCtrl);
vmCtrl.$inject = ['$state', 'API', 'serviceConfig'];
function vmCtrl($state, API, serviceConfig) {
var vm = this;
vm.formValues = {};
vm.submitForm = function() {
vm.formValues = {
"name": vm.formValues.name,
"age": "18",
"schoolId": vm.formValues.schoolId,
"mobno": "21234"
};
var url = serviceConfig.BASE_URL + 'details';
API.post(url, vm.formValues)
.then(submitForm_success)
.catch(submitForm_error);
}
}
})();
the following specs in karma returned success on testing,
it('should define vmCtrl', function() {
expect(vmCtrl).toBeDefined();
});
it('should define submitForm', function() {
expect(vmCtrl.submitForm).toBeDefined();
});
Now is it possible to test the values inside the object formValues? Like if I need to test the age using .toEqual(18)
How should I write the spec for this case?
You need to invoke the method first and then check the value.
it('should define submitForm', function () {
vmCtrl.submitForm();
expect(vmCtrl.formValues.age).toEqual(18);
expect(vmCtrl.formValues.mobno).toEqual(21234);
});
Yes, javascript doesn't really have any notion of private variables. So you can write the following:
it('should initialize formValues.age', function () {
expect(vmCtrl.formValues.age).toEqual(18);
});
Just be aware, that for that test to pass, you would first need to call vmCtrl.submitForm() and in order for that to work, you would need to properly mock API.post()
I have a service MyService with a function using the ga() event tracking call which I want to test:
angular.module('myModule').factory('MyService', [function() {
var myFunc = function() {
ga('send', 'event', 'bla');
// do some stuff
}
return {
myFunc: myFunc
}
]);
My spec file looks like this:
describe('The MyService', function () {
var MyService,
ga;
beforeEach(function () {
module('myModule');
ga = function() {};
});
beforeEach(inject(function (_MyService_) {
MyService = _MyService_;
}));
it('should do some stuff', function () {
MyService.myFunc();
// testing function
});
});
Running my tests always gives me:
ReferenceError: Can't find variable: ga
The problem is global scope of ga.
The ga variable that you create inside your tests has a local scope and will not be visible to your own service.
By using a global variable (ga) you have made unit testing difficult.
The current option would be to either create a angular service to wrap gaand use that everywhere else. Such service can be mocked too.
The other option is to override the global ga. But this will have side effects.
window.ga=function() {}
After trying different solution I finally fixed with below code.
beforeAll( ()=> {
// (<any>window).gtag=function() {} // if using gtag
(<any>window).ga=function() {}
})
Slightly out of date, but I am trying to leverage ReactGA and mocked creating an event like:
it('should do something...', () => {
const gaSpy = jest.spyOn(ReactGA, 'ga');
someService.functionThatSendsEvent({ ...necessaryParams });
expect(gaSpy).toHaveBeenCalledWith('send', 'event',
expect.objectContaining({/*whatever the event object is supposed to be*/}
);
});
This is helpful if youre sending specific data to an angular/reactjs service which is then sending it to GA.
Rest-angular for Api Calling .
My Aim to Write a Unit test Case by calling Controller and Test all the Scope are assigned,the Code blocks of with REST API Response But not MOCK RESPONSE.
Rest Angular Service :-
(function () {
angular.module('movieApp').service('movieApiService', callMoviesApi);
function callMoviesApi(Restangular) {
this.getMyMovie= function (Id) {
return Restangular.one('movies/' + movieId).get().then(function(result){
return result.plain();
});
};
this.getMoviesList = function () {
return Restangular.all('movies').getList().then(function(result){
return result.plain();
});
};
}
}());
Where I am Injecting this Service to Controller as a Dependency
Controller Code Follows :-
angular.module('movieApp').controller('MoviesController', ['$scope','movieApiService',
function ($scope, MovieService) {
$scope.movie = $stateParams.movieId;
MovieService.getMovieDetails($scope.movie).then(function (result) {
$scope.movieDetails = result;
$scope.movieId = result._id;
$scope.movieName = result.displayName;
});
}
]);
I did tried to Write a Unit test for the Above Controller not Going good :-(
Test Code Follows:-
'use strict';
(function() {
describe('MoviesController', function() {
//Initialize global variables
var scope,stateParams={},
MoviesController;
// Load the main application module
beforeEach(module('movieApp'));
beforeEach(inject(function($controller, $rootScope,$stateParams) {
scope = $rootScope.$new();
stateParams.movieId='Baahubali';
HomeController = $controller('MoviesController', {
$scope: scope,
$stateParams:stateParams
});
}));
it('Should call movieApi and Assign Scopes', function() {
var Api="http://testsite.com/moives/thor";
var myScope=$httpBackend.expectGET(Api).passthrough();
expect(scope.movie).toBeDefined();
console.log('****'+scope.movie.displayName);
});
});
})();
Error is Raising :-
Error: Unexpected request: GET http://testsite.com/movies/undefined
Expected GET http://testsite.com/movies/undefined?
at $httpBackend (C:/wrokingdir2015/public/lib/angular-mocks/angular-mocks.js:1245)
at sendReq (C:/wrokingdir2015/public/lib/angular-mocks/public/lib/angular/angular.js:9695)
Could Any One help me to Write a Unit test case Which can Initialize controller and Assing Scopes like in real controller for testing .
Honestly iam New Guy for Unit testing .
I suggest Selenium with Cucumber for having the scenarios that you test in a nice and readable format
but for testing only a REST api you just need an implementation of javax.ws.rs.client.Client, I use org.glassfish.jersey.client.JerseyClient.
private final Client client = ClientBuilder.newClient();
e.g.
#When("^I want to retrieve all cells for the report with id \"([^\"]*)\".$")
public void accessCellReport(String id) {
response = client.target(URL).path(PathConstants.PATH_ID)
.resolveTemplate(PathConstants.PARAM_ID, reportId).request(MediaType.APPLICATION_JSON).get();
RestAssertions.assertResponseOk(response);
}
First of all i would use Restangulars one method as it supposed to be used.
Read more about it here: https://github.com/mgonto/restangular#creating-main-restangular-object
Restangular.one('movies', movieId);
In my service test i would do something like this to test that the correct endpoint has been called.
it('should call /movies/{movieId}', function() {
var spy = sinon.spy(Restangular, 'one');
var movieId = 1;
movieApiService.getMyMovie(movieId);
expect(spy).to.have.been.calledWith('movies', movieId);
});
Then I would make a sinon stub to mock the reponse from the service in another test for the controller.
it('should set my movies variabel after calling movie service', function() {
var mockResponse = [
{
id: 1,
title: 'Titanic'
},
{
id: 2,
title: 'American History X'
}
];
sinon.stub(movieApiService, 'getMyMovie')
.returns(
$q.when(
[
{
id: 1,
title: 'Titanic'
},
{
id: 2,
title: 'American History X'
}
]
);
);
expect($scope.movieList).to.equal(mockResponse);
});
And another test for checking that the controller catch function is called.
it('should call error handling if service rejects promise', function() {
sinon.stub(movieApiService, 'getMyMovie')
.returns(
$q.reject('an error occured when fetching movie');
);
});
I suggest using Selenium:
http://www.seleniumhq.org/.
Easy to write unit tests and can be automatized with jenkins build.
I have a service CurrentUser that has a dependency on StorageFactory. In the constructor of this service, if StorageFactory.get returns a user, I am setting the user to that user, otherwise setting a default user value. Now, I want to test this.
I have managed to make this work, but I am not happy with the approach I am using. I got the inspiration for this approach here.
I have pasted the code below. If you prefer, I have also created a gist here. I have removed the irrelevant part of the code to avoid distraction. This is written using ES6 classes and modules, but that shouldn't make any difference to the tests.
The problem with the approach is that the mock will be used across all the tests, which may not be a bad thing but I want to control that. Is there a way to make this mock take affect only for this test?
One roadblock in finding better approach is that the mock has to be done before angular created the mock CurrentUserModule module. Is there a better way of testing this? I would appreciate any suggestions on this.
Service
import StorageFactoryModule from 'app/common/services/StorageFactory';
class CurrentUser {
constructor(StorageFactory) {
this.storageKey = 'appUser';
this.StorageFactory = StorageFactory;
this.profile = initializeUser.call(this);
function initializeUser() {
var userFromStorage = StorageFactory.get(this.storageKey);
if (userFromStorage != null) {
return userFromStorage;
} else {
// return default user
}
}
}
// more methods, that are removed for brevity
}
CurrentUser.$inject = ['StorageFactory'];
export default angular.module('CurrentUserModule', [StorageFactoryModule.name])
.service('CurrentUser', CurrentUser);
Test
import angular from 'angular';
import 'angular-mocks';
import './CurrentUser';
describe('CurrentUser', function () {
"use strict";
var user = {userid: 'abc', token: 'token'};
var storageFactoryMock = {
get: function (key) {
return user;
},
put: function (key, newUser) {
user = newUser;
},
remove: function (key) {
user = undefined;
}
};
beforeEach(function () {
angular.module('StorageFactoryModule')
.value('StorageFactory', storageFactoryMock);
angular.mock.module('CurrentUserModule');
});
it('should Initialize User from local storage if already exists there', inject(function (CurrentUser) {
expect(CurrentUser.profile).toEqual(user);
}))
});
I have managed to find a better way as below. I was initially doing it the wrong way (feels stupid now). Rather than creating a mock module, I was creating a real module that was overriding the original one, and that's why it was impacting all tests. Now, I am creating a mock module and using $provide to override StorageFactory, which will impact only the current suite.
beforeEach(function () {
angular.mock.module('StorageFactoryModule');
module(function($provide) {
$provide.value('StorageFactory', storageFactoryMock);
});
angular.mock.module('CurrentUserModule');
});
EDIT: Refactored my code and made it more flexible by creating a function that accepts a user as parameter and creates modules based on the user passed.
var correctUser = {userid: 'abc', token: 'token'};
var defaultUser = {userid: '', token: ''};
function createStorageFactoryMock(userInStorage) {
return {
get: function () {
return userInStorage;
},
put: function (key, newUser) {
userInStorage = newUser;
},
remove: function () {
userInStorage = undefined;
}
}
}
function CreateUserModule(user = correctUser) {
angular.mock.module('StorageFactoryModule');
module(function ($provide) {
$provide.value('StorageFactory', createStorageFactoryMock(user));
});
angular.mock.module('CurrentUserModule');
}
Now in my tests, I can mock different module for different scenarios, and write my tests accordingly. Any feedback is welcome.
it('should Initialize User from storageFactory if already exists in storage', function () {
CreateUserModule();
inject(function (CurrentUser) {
expect(CurrentUser.profile).toEqual(correctUser);
});
});
it('should Initialize default user if user not present in storage', function () {
CreateUserModule(null);
inject(function (CurrentUser) {
expect(CurrentUser.profile).toEqual(defaultUser);
});
});
I've been trying to write some unit tests for my services which use AngularFire to communicate with Firebase inside an Angular website.
I'm new to AngularJS and so I feel like I'm missing something obvious but couldn't find any great examples online (at least not that spoke to my limited knowledge).
I found some limited docs on MockFirebase https://github.com/katowulf/mockfirebase/tree/master/tutorials and that showed how to pretty much mock out the data so I did that.
For further examples of mockfirebase I looked at the angular fire's unit tests https://github.com/firebase/angularfire/tree/master/tests/unit but that didn't seem to show me the right way.
Here is my service --
app.service('Subscription', function ($firebase, FIREBASE_URL, $q) {
var ref;
var Subcription = {
ref: function () {
if (!ref) ref = new Firebase(FIREBASE_URL + "/subscriptions");
return ref;
},
validateSubscription: function(userId){
var defer = $q.defer();
$firebase(Subcription.ref().child(userId))
.$asObject()
.$loaded()
.then(function (subscription) {
defer.resolve(subscription.valid === true);
});
return defer.promise;
},
recordSubscription: function(userId){
return Subcription.ref().$set(userId, {valid: true});
}
};
return Subcription;
});
Here is the spec file --
describe('Service: subscription', function () {
// load the service's module
beforeEach(module('clientApp'));
// instantiate service
var subscription;
var scope;
beforeEach(inject(function (_Subscription_, $rootScope) {
MockFirebase.override();
subscription = _Subscription_;
scope = $rootScope.$new();
}));
it('allows access when the user id is in the subscription list', function () {
subscription.ref().push({'fakeUser': {valid: true}});
subscription.ref().flush();
var handler = jasmine.createSpy('success');
subscription.validateSubscription('fakeUser').then(handler);
scope.$digest();
expect(handler).toHaveBeenCalledWith(true);
});
});
It seems like the problem is that the promise never gets resolved inside of $asobject.$loaded because that angularfire part isn't happening.
I get the following as a result of the test: 'Expected spy success to have been called with [ true ] but it was never called.'