I am reading the Angular JS documentation I am looking at this example:
// testing controller
describe('MyController', function() {
var $httpBackend, $rootScope, createController;
beforeEach(inject(function($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
$httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
createController = function() {
return $controller('MyController', {'$scope' : $rootScope });
};
}));
My question is what purpose the createController serves, I don't really understand why it is there or what the last line does where $controller is returned or what it has to do with the $scope.
It is the second grey section that contains code underneath the header: Unit testing with mock $httpBackend.
Help would be greatly appreciated.
$controller returns an instance of MyController from the first grey section. To give the controller some context, it passes the $rootScope into the instantiation of the controller. Hence when you execute the controller (as shown in subsequent it() blocks) the controller runs and kicks off the $http.get('/auth.py') request.
Related
I'm testing directives that use the templateUrl property, and was wondering what the best way is to compile the templates. Is is better to use $templateCache or $httpBackend? I am guessing it's better to use $templateCache since I think this is the use case it was made for, but I've seen it done both ways. Although I haven't got the $httpBackend method fully functional yet.
NOTE: the second test is for the rewrite of the original project. The first test is from the original project.
The $templateCache way:
describe('buttonToggle', function() {
var elm, scope;
beforeEach(module('app'));
beforeEach(module('src/app/partials/buttonToggle/buttonToggle.html'));
beforeEach(inject(function($templateCache, _$compile_, _$rootScope_) {
template = $templateCache.get('src/app/partials/buttonToggle/buttonToggle.html');
$templateCache.put('src/app/partials/buttonToggle/buttonToggle.html', template);
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('should have an on/off switch', function() {
var buttonElement = angular.element('<button-toggle></button-toggle>');
var element = $compile(buttonElement)($rootScope);
$rootScope.$digest();
expect(element.text()).toContain('ON');
expect(element.text()).toContain('OFF');
});
});
My nonworking implementation of $httpBackend:
describe('The buttonToggle directive', function() {
var $compile,
$scope,
$httpBackend,
btElement,
btElementPath = 'client/modules/buttonToggle/buttonToggle.html',
btElementFileName = 'buttonToggle.html';
beforeEach(module('app'));
beforeEach(inject(function(_$compile_, _$rootScope_, _$httpBackend_) {
$compile = _$compile_;
$scope = _$rootScope_;
$httpBackend = _$httpBackend_;
$httpBackend.whenGET(btElementPath).respond(200);
$httpBackend.expectGET(btElementFileName).respond(200);
btElement = $compile('<button-toggle></button-toggle>')($scope);
$scope.$digest();
}));
it('should be defined', function() {
expect(btElement.html()).toContain('btn');
});
});
Also, any ideas on how to get the latter test to work? I don't think I setup the whenGET correctly since the error I am getting from my assertion states that the compiled element is empty.
Well I actually found a short answer from the Angular docs. If anyone else has good knowledge on this subject, then please feel free to answer, but this is what I found:
If your directive uses templateUrl, consider using karma-ng-html2js-preprocessor to pre-compile HTML templates and thus avoid having to load them over HTTP during test execution. Otherwise you may run into issues if the test directory hierarchy differs from the application's.
I am trying to make a real call and Assign Scopes for testing
Using passThrough Method but Throwing Error
Code Follows:-
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('w00App'));
var scope, MainCtrl, $httpBackend;
// Initialize the controller and a mock scope
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('http://api.some.com/testdata').passThrough();
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope
});
})); it('should make a post to refresh the friends list and return matching users', function(){
var deferredResponse = $httpBackend.expectGET('http://api.some.com/testdata').passThrough();
console.log('response'+JSON.stringidy(deferredResponse));
$httpBackend.flush();
// expect(deferredResponse).toEqual(deferredResponse);
}); });
Error :- TypeError: 'undefined' is not a function (near '...
').passThrough();...') .....
How can i call and Assign Scopes Like in Real controller ? pls Help.. it make my life Easy .
When testing a real controller and inside the controller you make some REST calls to the backed, it is best to mock those response calls, intercept the calls via $httpBackend object.
jasmine.getJSONFixtures().fixturesPath = 'base/test/unit/authz/api_mock/';
$httpBackend.when('POST', CONFIG.get('MAIN_URL_FOR_REST_SERVICES') + 'actions/search').respond(function() {
return [200, window.getJSONFixture('actions.json')];
});
at least, this is how I proceed in testing the controllers.
if you really really want to call the backed use:
$http.get(YOUR_URL).success(function(data) {
--- your test ---
});
and do not forget do inject the http service in the beforeEach method:
beforeEach(inject(function(_$http_) {
$http = _$http_;
}));
I want to make an integration test with real calls to my server, so, I don't want to use the $httpBackend module from angular-mocks, So I try this:
beforeEach(inject(function($rootScope,_MembersDataSvc_){
service = _MembersDataSvc_;
}));
it('test',function(done){
service.me().then(function(){done();});
});
And the service is:
function me() {
return $http
.get('urlBase/me')
.then(meSuccess);
function meSuccess(response) {
return response.data.members[0];
}
}
This never call the $http, it seems that angular-mocks override the $http service an never made the call.
Some ideas?
EDIT 1:
According to this post: http://base2.io/2013/10/29/conditionally-mock-http-backend/
you can make a passThrough for that $http calls that you don't want to mock, so y try this:
var service;
var scope;
var $httpBackend;
beforeEach(inject(function($rootScope,_MembersDataSvc_,_$httpBackend_){
service = _MembersDataSvc_;
scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
}));
it('test',function(done){
//this.timeout(10000);
$httpBackend.whenGET(/views\/\w+.*/).passThrough();
$httpBackend.whenGET(/^\w+.*/).passThrough();
$httpBackend.whenPOST(/^\w+.*/).passThrough();
service.me().then(function(response){console.log(response);done();});
scope.$apply();
//service.getDevices(member).then(function(response){console.log(response);done();})
});
But the passThrough is undefined here.
EDIT 2:
I read this post: http://blog.xebia.com/2014/03/08/angularjs-e2e-testing-using-ngmocke2e/, but I supose that is an stanalone test??, I want to run with karma and jasmine.
This is my entire test.
describe('integration test', function () {
beforeEach(function () {
module('MyAngularApp');
});
var service;
var scope;
var $httpBackend;
beforeEach(inject(function($rootScope,_MembersDataSvc_,_$httpBackend_){
service = _MembersDataSvc_;
scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
}));
it('test for test',function(done){
$httpBackend.whenGET(/views\/\w+.*/).passThrough();
$httpBackend.whenGET(/^\w+.*/).passThrough();
$httpBackend.whenPOST(/^\w+.*/).passThrough();
service.me().then(function(response){console.log(response);done();});
scope.$apply();
});
});
I recomend using ngMidwayTester that allows you to connect to the real backend, I use it to make integration tests on the code level - so something in between unit and e2e testing:
Two types of tests in AngularJS (plus one more) - Full-Spectrum Testing with AngularJS and Karma
I am trying to write an unit test for my app. It contains http request call in my case.
Test file
'use strict';
describe('Controller: testCtrl', function () {
// load the controller's module
beforeEach(module('myApp'));
var testCtrl, scope, $httpBackend;
beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_, $cookies) {
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
testCtrl = _$controller_('testCtrl', {
$scope: scope,
});
}));
it("should return product data", function() {
$httpBackend.whenGET('/api/store/' + productID + '/products').respond([{
//not sure what I should do next...
}])
})
controller file
$http.get(('/api/store/' + productID + '/products').success(
function(data) {
//the data could contain 10 objects with 10+ property within each object.
}
);
Since the http request return a very complex object, I am not sure how to write my test. Can anyone help me about it? Thanks a lot!
You assume that your API works correctly, and what you're trying to actually test is:
does your app respond to the URL it should?
does it do any processing of th data it should?
So return a mock object in your whenGET, with enough detail for any data processing code.
As far as the test goes, you will have to return a mock object response. That being said, you do not need to pollute your test case with your 1000 line mock JSON. Simply save it in a separate file and use karma-ng-json2js-preprocessor to return it from the whenGET.
I've coded a directive that checks some permissions and delete an element from the DOM if permissions are KO.
I'd love to unit test it, but... hem, I'm banging my head agains walls to make this simple test work.
I use $rootScope.digest() to compile a piece of html. When calling this function, angular tries to load my app main page and I get the dreaded "no more request expected" error.
So here is the test :
describe('Unit testing permission-needed', function() {
var $compile;
var $rootScope;
var $httpBackend;
// Load the myApp module, which contains the directive
beforeEach(module('app'));
beforeEach(angular.mock.module('ngMockE2E'));
beforeEach(inject(function(_$compile_, _$rootScope_, $injector) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$httpBackend = $injector.get('$httpBackend'); // not sur if I can inject it like others services ?
$httpBackend.whenGET('app/login/login.tpl.html').passThrough(); // this doesn't seem to work
}));
it('should replace the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<div permission-needed><span>Some content goes here</span></div>")($rootScope);
$rootScope.$digest(); // BAM => "no more request expected"
// Do the test here
// expect(....);
});
});
Note that if I use
.respond('some html here');
instead of
.passThrough() it works.
Thank you.
Well, answering myself :
using a $new() rootScope, test is passing :
$rootScope = _$rootScope_.$new();
Hope this help someone.