I am using Karma to test a function within an angular directive. The function executes fine but the http.get request in it seems to be ignored where as others throw an unexpected get error.
beforeEach(inject(function($injector, $compile, $rootScope){
$gCompile = $compile;
$gScope = $rootScope;
$httpBackend = $injector.get('$httpBackend');
$gHttp = $httpBackend;
}));
it("Should login to the UI", function() {
//Compile directive
var element = angular.element('<my-app></my-app>');
$gCompile(element)($gScope);
//Reference it's local scope
var dirScope = element.scope();
$gScope.$digest();
dirScope.login();
});
The get request is in the login function.
It might help if you can show us what the login() function looks like, but for now it looks like you're not using $httpBackend correctly.
Take a look at http://docs.angularjs.org/api/ngMock/service/$httpBackend.
You should set up an expectation on $httpBackend, then call your login() function, then call flush() on $httpBackend.
Related
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 new to AngularJS and JasmineJS.
I have decorated the Angular $log service to do logging over http. I verified that it works fine in the program itself. However, when running the Jasmine unit tests, the $log calls create errors. I get messages such as the following:
TypeError: TypeError: $log.warn.logs is undefined in
http...[snip]...angular-mocks.js (line 316)
The messages follow this pattern for all the different $log functions ($log.error.logs, $log.info.logs, etc.).
My current injector looks like this:
var $httpBackend, log;
beforeEach(inject(function ($injector, $log) {
log = $log;
//lots of mock http service things here
}));
I have also tried this:
beforeEach(inject(['$log', function (log) {
$log = log;
}]));
And this:
beforeEach(inject(function ($injector) {
log = $injector.get('$log');
// mock http things here
}));
A failing test looks like this:
it('should log error', function () {
$httpBackend.expectPOST('the url for logging');
var controller = createController();
log.warn('Test warning');
$httpBackend.flush();
});
All calls to $log in the code being tested result in the same error. When I commented them out and removed the failing test above, all unit tests passed.
How should I set up the unit tests correctly?
The answer is that there is something wrong with angular-mocks.js and it is not instantiating the log arrays correctly. I downloaded the latest non-beta copy, and it's still doing the same thing. The instantiation of arrays happens in their code using a call to $log.reset(), but somehow, that part of their code is not being reached. Therefore, I fixed my problem by manually calling $log.reset():
//This works
var $log;
beforeEach(inject(function ($injector) {
$log = $injector.get('$log');
$log.reset();
//continue other stuff
}));
//This also works
var log;
beforeEach(inject(function ($injector, $log) {
log = $log;
log.reset();
//continue other stuff
}));
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.
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.