Testing AngularJS controllers with resources

Background: I am writing unit test for angular js controllers that use angular $ resources wrapped in services (for ease of maintenance).

Controller example:

name = 'app.controllers.UsersIndexCtrl' angular.module(name, []) .controller(name, [ '$scope' '$location' '$dialog' 'Users' 'UserRoles' ($scope, $location, $dialog, Users, UserRoles) -> # Get users list $scope.users = Users.query() # rest... ]) 

Resource Services Example:

 angular.module('app.services.Users', []) .factory 'Users', ['$rootScope', '$http', '$resource', '$location' , ($rootScope, $http, $resource, $location)-> baseUrl = '/users' Users = $resource baseUrl + '/:userId', {userId: '@_id'} Users.getStatus = -> console.log 'User::getStatus()' req = $http.get baseUrl + '/status' req.success (res)-> $rootScope.globalUserAccountSettings = res unless $rootScope.$$phase then $rootScope.$apply() # other, custom methods go here... ]) 

Most unit test examples in angular suggest using $ httpBackend and thus make fun of the $ http service in controllers. Honestly, I doubt that this is a good practice, because if that happened, I would have to hard-code the request paths in all controller tests, and I want to isolate the behavior of the device. $ httpBackend mock is really great, but only if you use $ resource in controllers directly.

A typical single test using $ httpBackend would look like this:

 it 'should be able to add a new empty user profile', -> $httpBackend.flush() l = $scope.users.length $httpBackend.expect('POST', '/users').respond _.cloneDeep mockResponseOK[0] $scope.add() $httpBackend.flush() expect($scope.users.length).toEqual l + 1 

What if I created an instance of a user resource class, for example:

 angular.module('app.services.Users', []) .factory 'Users', -> class Users $save:(cb)-> $remove:-> @query:-> @get:-> 

Angular DI mechanisms will override the old app.services.Users module with this transparent way and enable me to run checks using jasmine spies.

I am concerned about the fact that I could not find a single example that supports my idea. So the question is which one would you use and why or what am I doing wrong?

+4
source share
3 answers

I think it’s far wiser to stifle this at the service level with Jasmine spies, as you suggested. You are testing the controller at this stage, and not at the service β€” the exact way in which the HTTP request is made should not be a problem for this test.

You can do something in your specification as follows:

 var Users = jasmine.createSpyObj('UsersStub', ['query', 'get']); beforeEach(inject(function($provide) { $provide.factory('Users', function(){ return Users; }); }); 

And then, in your respective tests, you can drown out individual service methods to return what you expect using methods like "andCallFake" on your spy object.

+4
source

The best you can do is make a fake resource using methods that were supposed to be called:

 var queryResponse = ['mary', 'joseph'], Users = function() { this.query = function() { return queryResponse; }, scope, HomeCtrl; }; beforeEach(inject(function($rootScope, $controller) { scope = $rootScope.$new(); HomeCtrl = $controller('HomeCtrl', {$scope: scope, Users: new Users()}); })); it('has users in scope', function() { expect(scope.users).toEqual(queryResponse); }); 
+1
source

I am new to this. I am writing my tests using coffeescript using dsl, but today I had a similar problem. The way I decided this was to create a jasmine spioli for my resource. Then I made a promise. When the promise is resolved, it will call the "success" function, which you will go through in the controller. Then in the β€œthis” method, I actually resolve the promise.

I think the code would look something like this using js and jasmine, but in fact I didn't have time to check

 beforeEach(inject(function($rootScope, $controller, $q ) { scope = $rootScope.$new(); queryPromise = $q.defer() User = jasmine.createSpyObject("UsersStub", ["query"]); User.query.andCallFake(function(success,errror){queryPromise.promise.then(success,error)}); HomeCtrl = $controller('HomeCtrl', {$scope: scope, Users: new Users()}); })); it('has users in scope', function() { queryPrmomise.resolve({User1: {name"joe"}) expect(scope.users).toEqual(queryResponse); }); 
0
source

Source: https://habr.com/ru/post/1484788/


All Articles