Make call to kinvey from angular test? - angularjs

I need to create a user in kinvey and assert that the user has been added, all within an angular test. I am using the karma test runner. All my of tests are timing out, and none of the code inside the kinvey promise blocks is being executed. How can I go about writing these tests? Test code is below:
describe("Kinvey: Users", function() {
var key,
secret;
beforeEach(function(){
key = '...',
secret = '...'
});
it("should create a user", function(done){
var App = angular.module('App', ['kinvey']);
App.run(['$kinvey', function($kinvey) {
$kinvey.init({
appKey: key,
masterSecret: secret
});
var promise = $kinvey.DataStore.save('users', {
username : 'gertrude#test.com'
});
promise().then(function(success){
var query = new $kinvey.Query();
query.equalTo('username', 'gertrude');
var queryPromise = $kinvey.DataStore.find('users', query);
return queryPromise().then(
function(response){
console.log("response");
expect(response.username).to.equal("gertrude#test.com");
var destroyPromise = $kinvey.DataStore.destroy('users', response.id);
return destroyPromise().then(function(success){
return done();
}, function(error){
return done();
})
}, function(error){
return null;
})
}, function(error){
return null;
});
}]);
});

You are missing a few things:
You are missing a call to angular.bootstrap(), so the run block is never executed.
The $kinvey.init() method is asynchronous. So, before calling $kinvey.DataStore.save() (or any other $kinvey.* method for that matter), make sure the init method finishes.
It seems you are creating users by using $kinvey.DataStore.save() to the users collection. It is highly recommended that you use $kinvey.User.signup() instead.
The best way to go forward is to move things to the before hook:
before(function(done) {
this.App = angular.module('App', ['kinvey']);
this.App.run(['$kinvey', function($kinvey) {
$kinvey.init({
appKey : 'App Key',
appSecret : 'App Secret'
}).then(function() {
done();
}, function(error) {
done(new Error(error.description));
});
}]);
angular.bootstrap(document, ['kinvey']);
});
Now, in your test (it method), you can obtain a reference to $kinvey by doing:
var $injector = angular.injector(['ng', 'kinvey']);
$injector.invoke(function($kinvey) {
// Do your tests with $kinvey.
});
Test away! I put a JSFiddle here.
Disclaimer: I am the author of the library.

Related

How can I Unit Test Rest Angular Service and Controller?

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.

angularjs unit test promise based service (SQlite database service)

I'm very new in unit testing angularjs applications and I think I don't understand the main concept of testing promise based services on angularjs.
I will directly start with my example:
I have a SQLite db-service which has this method:
var executeQuery = function(db,query,values,logMessage) {
return $cordovaSQLite.execute(db, query, values).then(function(res) {
if(res.rows.length>0) return res;
else return true;
}, function (err) {
return false;
});
};
And I want to write a test case, where I execute a query and then I want to get the return value of the executeQuery function of my service.
My test description is this:
describe("Test DatabaseCreateService‚", function () {
var DatabaseCreateService,cordovaSQLite,ionicPlatform,rootScope,q;
var db=null;
beforeEach(module("starter.services"));
beforeEach(module("ngCordova"));
beforeEach(module("ionic"));
beforeEach(inject(function (_DatabaseCreateService_, $cordovaSQLite,$ionicPlatform,$rootScope,$q) {
DatabaseCreateService = _DatabaseCreateService_;
cordovaSQLite = $cordovaSQLite;
ionicPlatform = $ionicPlatform;
q = $q;
rootScope = $rootScope;
ionicPlatform.ready(function() {
db = window.openDatabase("cgClientDB-Test.db", '1', 'my', 1024 * 1024 * 100);
});
}));
describe("Test DatabaseCreateService:createTableLocalValues", function() {
it("should check that the createTableLocalValues was called correctly and return correct data", function() {
var deferred = q.defer();
deferred.resolve(true);
spyOn(DatabaseCreateService,'createTableLocalValues').and.returnValue(deferred.promise);
var promise = DatabaseCreateService.createTableLocalValues(db);
expect(DatabaseCreateService.createTableLocalValues).toHaveBeenCalled();
expect(DatabaseCreateService.createTableLocalValues).toHaveBeenCalledWith(db);
expect(DatabaseCreateService.createTableLocalValues.calls.count()).toEqual(1);
promise.then(function(resp) {
expect(resp).not.toBe(undefined);
expect(resp).toBe(true);
},function(err) {
expect(err).not.toBe(true);
});
rootScope.$apply();
});
});
});
This test description works but it does not return the value from the function instead of it return what gets resolved in deferred.resolve(true);
What I want to do is the call the DatabaseCreateService.createTableLocalValues function and resolve the data which gets returned from the function.
The createTableLocalValues function is this:
var createTableLocalValues = function(db) {
var query = "CREATE TABLE IF NOT EXISTS `local_values` (" +
"`Key` TEXT PRIMARY KEY NOT NULL," +
"`Value` TEXT );";
return executeQuery(db,query,[],"Create cg_local_values");
};
Well if I run this method on browser or device I get a true back if everything works fine and the table gets created. So how do I get this real true also in the test description and not a fake true like in my example above?
Thanks for any kind of help.
Example 2 (with callThrough):
describe('my fancy thing', function () {
beforeEach(function() {
spyOn(DatabaseCreateService,'createTableSettings').and.callThrough();
});
it('should be extra fancy', function (done) {
var promise = DatabaseCreateService.createTableSettings(db);
rootScope.$digest();
promise.then(function(resp) {
console.log(resp);
expect(resp).toBeDefined();
expect(resp).toBe(true);
done();
},function(err) {
done();
});
});
});
Log message in karma-runner:
LOG: true
Chrome 46.0.2490 (Mac OS X 10.11.1) Test DatabaseCreateService‚ testing createTable functions: my fancy thing should be extra fancy FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Chrome 46.0.2490 (Mac OS X 10.11.1): Executed 42 of 42 (1 FAILED) (8.453 secs / 8.03 secs)
UPDATE:
It turned out that this problem has something to do with the $cordovaSQLite.executeQuery function itself. Somehow it have no timeout on the promise and thats what the error causes. I changed the execute function of ng-cordova to this. (hoping that this change does not break anything working)
execute: function (db, query, binding) {
var q = Q.defer();
db.transaction(function (tx) {
tx.executeSql(query, binding, function (tx, result) {
q.resolve(result);
},
function (transaction, error) {
q.reject(error);
});
});
return q.promise.timeout( 5000, "The execute request took too long to respond." );
}
With that change the tests passes correctly!
You can spy on a function, and delegate to the actual implementation, using
spyOn(DatabaseCreateService,'createTableLocalValues').and.callThrough();
You might also need to call rootScope.$digest() after you call your function, so the promise will resolve.
Edit:
When testing async code, you should use the done pattern:
it('should be extra fancy', function (done) {
var promise = DatabaseCreateService.createTableSettings(db);
rootScope.$digest();
promise.then(function(resp) {
console.log(resp);
expect(resp).toBeDefined();
expect(resp).toBe(false);
expect(resp).toBe(true);
done();
});
});
A suggestion on the way you're asserting in your test:
In your test, you are calling then on your returned promise in order to make your assertions:
promise.then(function(resp) {
expect(resp).not.toBe(undefined);
expect(resp).toBe(true);
},function(err) {
expect(err).not.toBe(true);
});
Which is forcing you to add an assertion in an error function so that your test still fails if the promise doesn't resolve at all.
Try using Jasmine Promise Matchers instead. It will make your test code that easier to read and lead to clearer error messages when your tests fail. Your test would look something like this:
expect(promise).toBeResolvedWith(true);

AngularJS testing with firebase/mockfirebase in a service

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.'

Mocking the $injector Service

I have a situation where I want to add services inside a module, as I may not know what they are beforehand. From looking at the docs, it seems that the only way to do this (without global scope) is with Angular's $injector service. However, it seems that this service is not mockable, which makes sense as it is the way Angular itself gets the dependencies, which are still important even in testing.
Essentially, I am emulating NodeJS's passport module. I want to have something like a keychain, where you add or remove an account during runtime. So far, I have this:
angular.module('myModule').factory('accounts', function($injector) {
return {
add: function(name) {
if(!$injector.has(name) {
$log.warn('No Angular module with the name ' + name + ' exists. Aborting...');
return false;
}
else {
this.accounts[name] = $injector.get(name);
return true;
}
},
accounts: []
};
});
However, whenever I try to mock the $injector function in Jasmine, like this:
describe('accounts', {
var $injector;
var accounts;
beforeEach(function() {
$injector = {
has: jasmine.createSpy(),
get: jasmine.createSpy()
};
module(function($provide) {
$provide.value('$injector', $injector);
});
module('ngMock');
module('myModule');
inject(function(_accounts_) {
accounts = _accounts_;
});
});
describe('get an account', function() {
describe('that exists', function() {
beforeEach(function() {
$injector.has.and.returnValue(true);
});
it('should return true', function() {
expect(accounts.add('testAccount')).toEqual(true);
});
});
describe('that doesn't exist', function() {
beforeEach(function() {
$injector.has.and.returnValue(false);
});
it('should return true', function() {
expect(accounts.add('testAccount')).toEqual(false);
});
});
});
});
the 2nd test fails because the accounts service is calling the actual $injector service, and not the mock. I can confirm this by calling $injector.get or $injector.has during the test or in the service itself.
What should I do? There seems to be no other way to add new dependencies, but this is exactly what I want to do. Am I wrong? Is there in fact another way to do this, without using $injector?
Assuming I am right, and there is no other way to do what I want to do, how should I go about testing this function? I could just trust that the $injector service does its job, but I still want to mock it for the tests. I could manually add the dependencies during the inject function, but that doesn't replicate the actual behavior. I could just not test the function, but then I wouldn't be testing the function.

chai-as-promised erroneously passes tests

I'm trying to write tests for a method that returns an angular promise ($q library).
I'm at a loss. I'm running tests using Karma, and I need to figure out how to confirm that the AccountSearchResult.validate() function returns a promise, confirm whether the promise was rejected or not, and inspect the object that is returned with the promise.
For example, the method being tested has the following (simplified):
.factory('AccountSearchResult', ['$q',
function($q) {
return {
validate: function(result) {
if (!result.accountFound) {
return $q.reject({
message: "That account or userID was not found"
});
}
else {
return $q.when(result);
}
}
};
}]);
I thought I could write a test like this:
it("it should return an object with a message property", function () {
promise = AccountSearchResult.validate({accountFound:false});
expect(promise).to.eventually.have.property("message"); // PASSES
});
That passes, but so does this (erroneously):
it("it should return an object with a message property", function () {
promise = AccountSearchResult.validate({accountFound:false});
expect(promise).to.eventually.have.property("I_DONT_EXIST"); // PASSES, should fail
});
I am trying to use the chai-as-promised 'eventually', but all my tests pass with false positives:
it("it should return an object", function () {
promise = AccountSearchResult.validate();
expect(promise).to.eventually.be.an('astronaut');
});
will pass. In looking at docs and SO questions, I have seen examples such as:
expect(promise).to.eventually.to.equal('something');
return promise.should.eventually.equal('something');
expect(promise).to.eventually.to.equal('something', "some message about expectation.");
expect(promise).to.eventually.to.equal('something').notify(done);
return assert.becomes(promise, "something", "message about assertion");
wrapping expectation in runs() block
wrapping expectation in setTimeout()
Using .should gives me Cannot read property 'eventually' of undefined. What am I missing?
#runTarm 's suggestions were both spot on, as it turns out. I believe that the root of the issue is that angular's $q library is tied up with angular's $digest cycle. So while calling $apply works, I believe that the reason it works is because $apply ends up calling $digest anyway. Typically I've thought of $apply() as a way to let angular know about something happening outside its world, and it didn't occur to me that in the context of testing, resolving a $q promise's .then()/.catch() might need to be pushed along before running the expectation, since $q is baked into angular directly. Alas.
I was able to get it working in 3 different ways, one with runs() blocks (and $digest/$apply), and 2 without runs() blocks (and $digest/$apply).
Providing an entire test is probably overkill, but in looking for the answer to this I found myself wishing people had posted how they injected / stubbed / setup services, and different expect syntaxes, so I'll post my entire test.
describe("AppAccountSearchService", function () {
var expect = chai.expect;
var $q,
authorization,
AccountSearchResult,
result,
promise,
authObj,
reasonObj,
$rootScope,
message;
beforeEach(module(
'authorization.services', // a dependency service I need to stub out
'app.account.search.services' // the service module I'm testing
));
beforeEach(inject(function (_$q_, _$rootScope_) {
$q = _$q_; // native angular service
$rootScope = _$rootScope_; // native angular service
}));
beforeEach(inject(function ($injector) {
// found in authorization.services
authObj = $injector.get('authObj');
authorization = $injector.get('authorization');
// found in app.account.search.services
AccountSearchResult = $injector.get('AccountSearchResult');
}));
// authObj set up
beforeEach(inject(function($injector) {
authObj.empAccess = false; // mocking out a specific value on this object
}));
// set up spies/stubs
beforeEach(function () {
sinon.stub(authorization, "isEmployeeAccount").returns(true);
});
describe("AccountSearchResult", function () {
describe("validate", function () {
describe("when the service says the account was not found", function() {
beforeEach(function () {
result = {
accountFound: false,
accountId: null
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
message = "PROMISE REJECTED";
reasonObj = arg;
});
// USING APPLY... this was the 'magic' I needed
$rootScope.$apply();
});
it("should return an object", function () {
expect(reasonObj).to.be.an.object;
});
it("should have entered the 'catch' function", function () {
expect(message).to.equal("PROMISE REJECTED");
});
it("should return an object with a message property", function () {
expect(reasonObj).to.have.property("message");
});
// other tests...
});
describe("when the account ID was falsey", function() {
// example of using runs() blocks.
//Note that the first runs() content could be done in a beforeEach(), like above
it("should not have entered the 'then' function", function () {
// executes everything in this block first.
// $rootScope.apply() pushes promise resolution to the .then/.catch functions
runs(function() {
result = {
accountFound: true,
accountId: null
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
reasonObj = arg;
message = "PROMISE REJECTED";
});
$rootScope.$apply();
});
// now that reasonObj has been populated in prior runs() bock, we can test it in this runs() block.
runs(function() {
expect(reasonObj).to.not.equal("PROMISE RESOLVED");
});
});
// more tests.....
});
describe("when the account is an employee account", function() {
describe("and the user does not have EmployeeAccess", function() {
beforeEach(function () {
result = {
accountFound: true,
accountId: "160515151"
};
AccountSearchResult.validate(result)
.then(function() {
message = "PROMISE RESOLVED";
})
.catch(function(arg) {
message = "PROMISE REJECTED";
reasonObj = arg;
});
// digest also works
$rootScope.$digest();
});
it("should return an object", function () {
expect(reasonObj).to.be.an.object;
});
// more tests ...
});
});
});
});
});
Now that I know the fix, it is obvious from reading the $q docs under the testing section, where it specifically says to call $rootScope.apply(). Since I was able to get it working with both $apply() and $digest(), I suspect that $digest is really what needs to be called, but in keeping with the docs, $apply() is probably 'best practice'.
Decent breakdown on $apply vs $digest.
Finally, the only mystery remaining to me is why the tests were passing by default. I know I was getting to the expectations (they were being run). So why would expect(promise).to.eventually.be.an('astronaut'); succeed? /shrug
Hope that helps. Thanks for the push in the right direction.

Resources