I am relatively new in Angular.js so I tried every other solution but it always throws an error assetDetailController is not a function got undefined.. Any advice I tried all the possible solution for this question on this site..
describe('asset detail Controller',
function () {
describe('assetDetailController',
function () {
var scope;
var rportService;
var controller;
var $q;
var deferred, assetId, rootScope;
var httpMock, data;
beforeEach(module('app'));
// inject the required services and instantiate the controller
//beforeEach();
describe('asset detail Controller 2',
function () {
deferred = null;
//beforeEach();
//beforeEach(angular.mock.inject(function ($q) {
//}));
beforeEach();
it('test to be called with',
function ( done) {
angular.mock.inject(function ($rootScope, $controller, reportService, $q, $httpBackend) {
scope = $rootScope.$new();
rportService = reportService;
controller = $controller('assetDetailController',
{
$scope: scope,
assetId: assetId,
reportService: rportService,
rootScope: $rootScope
});
Related
I have a following piece of code (simplified):
angular
.module('myApp')
.controller('MyController', MyController);
function MyController(wordService) {
getWord();
function getWord() {
return wordService.getNextWord()
.then(doSomethingWithWord)
.catch(doSomethingFailure);
function doSomethingWithWord(response) {
// ... something
}
function doSomethingFailure() {
// ... failing
}
}
}
And I have to test it.
I'm struggling with this over a day now and I can't get it working :(
How to test this code?
For the future, I figured it out:
I have to use $q service and request Angular digest cycle.
describe('MyController', function () {
var $controller, myController, wordService, $q, deferredResponse, scope;
beforeEach(function() {
module('myApp');
inject(function(_$controller_, _wordService_, _$q_, $rootScope) {
$controller = _$controller_;
wordService = _wordService_;
scope = $rootScope.new();
$q = _$q_;
});
myController = $controller('MyController', {wordService:wordService});
deferredResponse = $q.defer(); //deferring asynchronous response
spyOn(wordService, 'getNextWord').and.returnValue(deferredResponse.promise);
});
describe('Testing WordService', function() {
it('Should get next word', function () {
deferredResponse.resolve({status: 200, data: {word: 123}});
scope.$apply();
expect(wordService.getNextWord).toHaveBeenCalled();
})
})
});
I'm trying to set up Karma unit tests and in my test I want to set a scope variable so I can run the test. I get the error Cannot set property 'expandedSeries' of undefined.
Below is my code. What am I doing wrong?
describe('FormController', function () {
beforeEach(module('userFormApp'));
var $controller;
var $rootScope;
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('$scope.getImageSrc', function () {
var $scope, controller;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('FormController', { $scope: $scope});
});
$scope.expandedSeries = 1;
it('sets variables ', function () {
expect($scope).toBeDefined();
expect($scope.expandedSeries).toBeDefined();
expect($scope.expandedSeries).toEqual(1);
});
});
Instantiate the variable in before each so that the test cases can get it when they start.
describe('$scope.getImageSrc', function () {
var $scope, controller;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('FormController', { $scope: $scope});
$scope.expandedSeries = 1;
});
it('sets variables ', function () {
expect($scope).toBeDefined();
expect($scope.expandedSeries).toBeDefined();
expect($scope.expandedSeries).toEqual(1);
});
});
I have a method defined in AngularJS controller which is called on initialization. I want to test it using Jasmine ("jasmine-core": "^2.3.4", "karma": "^0.12.37"). I follow some tutorials on the Internet and StackOverflow questions, but I cannot find the right answer. Please take a look at this code:
Controller usersAddUserController:
(function () {
'use strict';
angular.module('app.users.addUser')
.controller('usersAddUserController', ['$scope', 'usersAddUserService', function ($scope, usersAddUserService) {
usersAddUserService.getCountryPhoneCodes().then(function (phoneCodes) {
$scope.phoneCodes = phoneCodes;
});
}]);
}());
Jasmine test:
(function () {
'use strict';
describe('usersAddUserControllerUnitTest', function () {
var scope, deferred, objectUnderTest, mockedAddUserService;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $q, $controller) {
scope = $rootScope.$new();
function emptyPromise() {
deferred = $q.defer();
return deferred.promise;
}
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
objectUnderTest = $controller('usersAddUserController', {
$scope: scope,
usersAddUserService: mockedAddUserService
});
}));
it('should call getCountryPhoneCodes method on init', function () {
//when
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
deferred.resolve();
scope.$root.$digest();
//then
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
}());
After running the tests, the error message is:
PhantomJS 1.9.8 (Windows 7 0.0.0) usersAddUserControllerUnitTest should call getCountryPhoneCodes method on init FAILED
Expected spy getCountryPhoneCodes to have been called.
I obviously missing something, but I cannot figure out what it is. Any help will be appreciated.
You are spying on the mock after it has been passed into the instantiated controller.
Try this:
describe('usersAddUserControllerUnitTest', function () {
var scope, deferred, objectUnderTest, mockedAddUserService, $controller;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $q, _$controller_) {
scope = $rootScope.$new();
function emptyPromise() {
deferred = $q.defer();
return deferred.promise;
}
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
$controller = _$controller_;
}));
function makeController() {
objectUnderTest = $controller('usersAddUserController', {
$scope: scope,
usersAddUserService: mockedAddUserService
});
}
it('should call getCountryPhoneCodes method on init', function () {
//when
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
makeController();
deferred.resolve();
scope.$root.$digest();
//then
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
EDIT Thanks #juunas for noticing the bug in my solution
You can provide the mock like this:
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
EDIT:
The code should look (as i cannot test it) like this:
(function () {
'use strict';
describe('usersAddUserControllerUnitTest', function () {
beforeEach(module('app'));
var emptyPromise = function() {
var deferred = $q.defer();
return deferred.promise;
}
var mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
var scope;
beforeEach(inject(function ($rootScope, $q, $controller) {
scope = $rootScope.$new();
$controller('usersAddUserController', {
$scope: scope
});
}));
it('should call getCountryPhoneCodes method on init', function () {
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
scope.$root.$digest();
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
}());
I am trying to write a unit test to test that tests a factory that performs a http.get and then tests the scope bindings.
The factory is called within my controller.
Here's a plunker showing my http.get: http://plnkr.co/edit/VqUSeTiEj3MP37tAXKad?p=preview
Ctrl:
app.controller('MainCtrl', function($scope, $http, factoryGetJSONFile) {
$scope.name = 'World';
factoryGetJSONFile.getMyData(function(data) {
$scope.Addresses = data.Addresses.AddressList;
$scope.People = data.Names.People;
$scope.Country = data.Country;
});
});
Test:
describe('with httpBackend', function () {
var app;
beforeEach(function () {
app = angular.mock.module('plunker')
});
describe('MyCtrl', function () {
var scope, ctrl, theService, httpMock;
beforeEach(inject(function ($controller, $rootScope, factoryGetJSONFile, $httpBackend) {
httpMock = $httpBackend;
scope = $rootScope.$new();
ctrl = $controller('MyCtrl', {
$scope: scope,
factoryGetJSONFile: factoryGetJSONFile,
$httpBackend: httpMock
});
}));
it("should make a GET call to data.json", function () {
console.log("********** SERVICE ***********");
httpMock.expectGET("data.json?").respond(data);
console.log(data.Addresses);
console.log(data.Names);
console.log(data.Country);
//expect(factoryGetJSONFile.getMyData()).toBeDefined();
httpMock.flush();
});
})
});
The test for the http.get seems ok, but when i try logging the reponse (data), an error occurs.
UPDATE:
When i try to log the call via:
console.log(httpMock.expectGET("data.json?").respond(data));
Undefined is displayed.
After much reading, it seems that the recommended way to call a web service from an AngularJS controller is to use a factory and return a promise from that.
Here I have a simple factory which calls a sample API.
myApp.factory('MyFactory', ['$http',function($http) {
var people = {
requestPeople: function(x) {
var url = 'js/test.json';
return $http.get(url);
}
};
return people;
}]);
And this is how I call it in the controller
myApp.controller('MyCtrl1', ['$scope', 'MyFactory', function ($scope, MyFactory) {
MyFactory.requestPeople(22).then(function(result) {
$scope.peopleList = result;
});
}]);
While it works fine, I would like to be able to mock the result that is passed in when then is called. Is this possible?
My attempt so far has produced nothing. This is my attempt:
//Fake service
var mockService = {
requestPeople: function () {
return {
then: function () {
return {"one":"three"};
}
}
}
};
//Some setup
beforeEach(module('myApp.controllers'));
var ctrl, scope;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('MyCtrl1', { $scope: scope, MyFactory: mockService });
}));
//Test
it('Event Types Empty should default to false', inject(function () {
expect(scope.peopleList.one).toBe('three');
}));
The error that I get when running this in karma runner, is
TypeError: 'undefined' is not an object (evaluating 'scope.peopleList.one')
How can I get this test working with my mocked data?
I don't think $httpBackend is what you're after here, you want the whole factory to be mocked without it having a dependency on $http?
Take a look at $q, in particular the code sample under the Testing header. Your issue might be resolved with code that looks like this:
'use strict';
describe('mocking the factory response', function () {
beforeEach(module('myApp.controllers'));
var scope, fakeFactory, controller, q, deferred;
//Prepare the fake factory
beforeEach(function () {
fakeFactory = {
requestPeople: function () {
deferred = q.defer();
// Place the fake return object here
deferred.resolve({ "one": "three" });
return deferred.promise;
}
};
spyOn(fakeFactory, 'requestPeople').andCallThrough();
});
//Inject fake factory into controller
beforeEach(inject(function ($rootScope, $controller, $q) {
scope = $rootScope.$new();
q = $q;
controller = $controller('MyCtrl1', { $scope: scope, MyFactory: fakeFactory });
}));
it('The peopleList object is not defined yet', function () {
// Before $apply is called the promise hasn't resolved
expect(scope.peopleList).not.toBeDefined();
});
it('Applying the scope causes it to be defined', function () {
// This propagates the changes to the models
// This happens itself when you're on a web page, but not in a unit test framework
scope.$apply();
expect(scope.peopleList).toBeDefined();
});
it('Ensure that the method was invoked', function () {
scope.$apply();
expect(fakeFactory.requestPeople).toHaveBeenCalled();
});
it('Check the value returned', function () {
scope.$apply();
expect(scope.peopleList).toBe({ "one": "three" });
});
});
I've added some tests around what $apply does, I didn't know that until I started playing with this!
Gog