My actual controller has a few more services, I made a strip down version in an attempt to track down this service injection issue.
Any ideas?
var testApp = angular.module("testApp",[]);
testApp.controller('testCtrl',['$scope','domSvc',testCtrl]);
function testCtrl($scope,domSvc){
$scope.testone = function(){
return "testone";
};
}
----------
describe('Main tests', function() {
beforeEach(angular.mock.module('testApp'));
var $controller,
$injector,
domSvc;
beforeEach(angular.mock.inject(function(_$controller_,_$injector_){
$controller = _$controller_;
$injector = _$injector_;
}));
it('testone should return the string testone',function(){
var $scope = {};
var domSvc = $injector.get('domSvc');
var controller = $controller('testCtrl',{$scope : $scope, domSvc : domSvc});
expect($scope.testone()).toEqual('testone');
});
});
Related
I have a controller that use a service, resolve a promise and then set a variable.
I want to test if the variable has been set, How I do this?
Service.js
angular
.module('myModule')
.service('MyService',['$resource',function($resource){
var r = $resource('/users/:id',{id:'#id'});
this.post = function(_user){
return (new r(_user)).$save()
}
}])
Controller
angular
.module('myModule')
.controller('myCtrl', ['MyService',function(MyService){
var vm = this;
vm.value = 0;
vm.post = function(_user){
MyService.post(_user).then(function(){
vm.value = 1;
});
};
}])
controller_spec
describe('Test Controller',function(){
beforeEach(module('myModule'));
beforeEach(inject(function($controller, MyService){
this.ctrl = $controller('myCtrl');
}))
it('after save value is 1',function(){
this.ctrl.post({name: 'chuck'});
//Always this.ctrl.value it's equal to 0
expect(this.ctrl.value).toBeEqual(1);
});
});
mock the service method and return a promise, then resolve it when you need it.
describe('Test Controller',function(){
var postDefer;
beforeEach(module('myModule'));
beforeEach(inject(function($controller, MyService, $q){
postDefer = $.defer();
spyOn(MyService, 'post').and.returnValue(postDefer.promise);
this.ctrl = $controller('myCtrl', {MyService : MyService});
}))
it('after save value is 1',function(){
this.ctrl.post({name: 'chuck'});
postDefer.resolve();
scope.$apply();
expect(this.ctrl.value).toBeEqual(1);
});
});
I'm very new to the AngularJs unit testing with Jasmine.So could you tell me how can I test below mentioned controller and countyService.getAllCountiesAsync() method using Jasmine.Thanks in advance.
Note : The controller below is having more than 50 injected services (I have shown few below).So I don't know which method is good for mock those also ?
Controller :
(function () {
appModule.controller('myController', [
'$scope', '$modalInstance', 'abp.services.app.property', 'abp.services.app.county', 'abp.services.app.propertyClass', 'abp.services.app.schoolDistrict'
function ($scope, $modalInstance, propertyService, countyService, propertyClassService, schoolDistrictService) {
vm.getAllCounties = function () {
countyService.getAllCountiesAsync().success(function (result) {
vm.counties = result.items;
});
};
vm.getAllCounties();
} ]);
})();
WebApi method :
public async Task<ListResultOutput<CountyListDto>> GetAllCountiesAsync()
{
var counties = await _countyRepository
.GetAllListAsync();
return new ListResultOutput<CountyListDto>(counties.OrderBy(o => o.Name).MapTo<List<CountyListDto>>());
}
You should write test cases for service and controller.
For services 'Daan van Hulst' has already given answer and for controller see below code:
describe('service tests', function () {
var $compile,$controller,myController, $rootScope, propertyService, countyService, propertyClassService, schoolDistrictService;
//All module dependencies
beforeEach(module('your-app-name'));
//inject required services and _$controller_ to create controller
beforeEach(inject(function(_$compile_,_$controller_, _$rootScope_, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_; // This is IMP
countyService = _countyService_;
// remianig services
// Now create controller
myController = $controller('myController', {
$scope : scope,
propertyService : propertyService // all other services
});}
it('should test something', function() {
spyOn(countyService, 'getAllCountiesAsync').and.callFake(function () {
var d = q.defer();
d.resolve({ items: [{data:'somedata'}] });
return d.promise;
});
myController.getAllCounties();
expect(myController.counties).not.toBe(null);
});
Update
I might have made mistakes, but this is the idea:
describe('service tests', function () {
var $compile, $rootScope, scope, vm, propertyService, countyService, propertyClassService, schoolDistrictService;
beforeEach(module('your-app-name'));
beforeEach(inject(function(_$compile_, _$rootScope_, $controller, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
propertyService = _propertyService_;
countyService = _countyService_;
propertyClassService = _propertyClassService_;
schoolDistrictService = _schoolDistrictService_;
vm = $controller('myController', {'$scope': scope})
spyOn(countyService, "getAllCountiesAsync").and.callFake(function() {
var deferred = $q.defer();
deferred.resolve({data: [{id:0}]});
return deferred.promise;
});
}));
it('can do remote call', inject(function() {
//Arrange
result = [{id:0}];
// Act
vm.getAllCounties();
// Assert
expect(vm.counties).toBe(result); //assert to whatever is resolved in the spyOn function
});
});
}
I assume that you create Angular services for all your services and that you app is working. Then, you can inject them in your tests:
describe('service tests', function () {
var $compile, $rootScope, propertyService, countyService, propertyClassService, schoolDistrictService;
beforeEach(module('your-app-name'));
beforeEach(inject(function(_$compile_, _$rootScope_, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
propertyService = _propertyService_;
countyService = _countyService_;
propertyClassService = _propertyClassService_;
schoolDistrictService = _schoolDistrictService_;
}));
it('should test something', function() {
expect(propertyService).toBeDefined();
expect(countyService).toBeDefined();
expect(propertyClassService).toBeDefined();
expect(schoolDistrictService).toBeDefined();
});
});
Update
I accidentally posted my solution in the answer above, so corrected it now. You can create your controller with $controller and pass in a scope object. You can also pass in any other dependencies. Then create a spy on the service, and once it gets called, call a different function which resolves a promise with mock data:
describe('service tests', function () {
var $compile, $rootScope, scope, vm, propertyService, countyService, propertyClassService, schoolDistrictService;
beforeEach(module('your-app-name'));
beforeEach(inject(function(_$compile_, _$rootScope_, $controller, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
propertyService = _propertyService_;
countyService = _countyService_;
propertyClassService = _propertyClassService_;
schoolDistrictService = _schoolDistrictService_;
// Create the controller, and pass in the scope with possible variables that you want to mock.
vm = $controller('myController', {'$scope': scope})
//Create a spy on your getAllCountiesAsync function and make it return a mock promise with mock data.
spyOn(countyService, "getAllCountiesAsync").and.callFake(function() {
var deferred = $q.defer();
deferred.resolve({data: [{id:0}]});
return deferred.promise;
});
}));
it('can do remote call', inject(function() {
//Arrange
result = [{id:0}];
// Act
vm.getAllCounties();
//I think that you also have to do this, but I am not a 100% sure.
scope.$apply();
// Assert
expect(vm.counties).toBe(result); //assert to whatever is resolved in the spyOn function
});
});
}
If my unit test is calling function which is in controller and that function is having a service call to fetch the details. Will it call service(StationService)?
My Karma unit test is not able to inject StationService and not able to call service.
My Code.
/// controller
var policyControllers = angular.module('policyControllers', []);
policyControllers.controller('StationListController', ['$translate', '$scope','$rootScope','$state', 'StationService', 'StationListExportService', function ($translate, $scope, $rootScope, $state, StationService, StationListExportService) {
...
$scope.getFilterDetails = function(StationService, filterDetails ){
StationService.get(filterDetails).$promise.then(function (filteredDetails) {
console.log(" Web services Result - ", JSON.stringify(filteredDetails));
},function(error) {
console.log(" Error ");
});
};
///Service
var policyServices = angular.module('policyServices', ['ngResource']);
policyServices.factory('StationService', ['$resource', function($resource) {
return $resource(policyConfig.mock? 'modules/policymanager/services/mock/stations.json': 'http://10.132.240.25:7640/policy/api/v1/stationpolicy/stations',{},{
get:{method: 'POST',isArray: false, url:'modules/policymanager/services/mock/stations.json'}
});
}]);
/// Unit test
describe('station filter', function(){
var scope;
var ctrl;
var translate, scope, rootScope, state;
var StationService, StationListExportService;
beforeEach(module('policyServices'));
beforeEach(module('policyControllers'));
beforeEach(inject(function(_StationService_, _StationListExportService_, $rootScope, $controller, $translate, $state) {
StationService = _StationService_;
StationListExportService = _StationListExportService_;
translate = $translate;
rootScope = $rootScope;
state = $state;
scope = $rootScope.$new();
ctrl = $controller('StationListController', {$scope: scope});
}));
it('Stations Inject test case', inject(['StationService',function(StationService){
var data = {"recency":"","countries":[],"policies":[],"stations":[{"stationName":"Test"}],"status":"ready","regions":[]};
scope.getFilterDetails(StationService, data);
/// Getting StationService is undifiened
}]));
Try using only one application say policyServices and use the same to define the controller as well. In addition to it try defining your service before you use them in your controller.
var policyServices = angular.module('policyServices', ['ngResource']);
policyServices.factory('StationService', ['$resource', function($resource)
{
return $resource(policyConfig.mock? 'modules/policymanager/services/mock/stations.json': 'http://10.132.240.25:7640/policy/api/v1/stationpolicy/stations',{},{
get:{method: 'POST',isArray: false, url:'modules/policymanager/services/mock/stations.json'}
});
}]);
policyServices.controller('StationListController', ['$translate', '$scope','$rootScope','$state', 'StationService', 'StationListExportService', function ($translate, $scope, $rootScope, $state, StationService, StationListExportService) {
...
$scope.getFilterDetails = function(StationService, filterDetails ){
StationService.get(filterDetails).$promise.then(function (filteredDetails) {
console.log(" Web services Result - ", JSON.stringify(filteredDetails));
},function(error) {
console.log(" Error ");
});
};
I've read a couple of threads on this but can't figure out a solution to my problem. In my controller I have a function:
this.dName = function() {
var url = $location.absUrl();
var matches = url.match(/\/dName\/(.*)$/);
return matches[1];
};
This causes my tests to fail as there is no absolute url to grab. How would I go about mocking $location.absUrl so that my tests can pass?
For example you could spy on the $location service, in jasmine that would be:
describe('MyController', function(){
beforeEach(module('myApp'));
var MyController, scope, $location;
beforeEach(inject(function($injector) {
var $controller = $injector.get('$controller');
var $rootScope = $injector.get('$rootScope');
$location = $injector.get('$location');
scope = $rootScope.$new();
MyController = $controller('MyController', {
$scope: scope,
});
}));
it('Check dName', function () {
spyOn($location, 'absUrl').and.returnValue(["match0", "match1"]);
var match = scope.dName();
expect(match).toEqual("match1");
expect($location.absUrl).toHaveBeenCalled();
});
});
We have few methods in Angular Controller, which are not on the scope variable.
Does anyone know, how we can execute or call those methods inside Jasmine tests?
Here is the main code.
var testController = TestModule.controller('testController', function($scope, testService)
{
function handleSuccessOfAPI(data) {
if (angular.isObject(data))
{
$scope.testData = data;
}
}
function handleFailureOfAPI(status) {
console.log("handleFailureOfAPIexecuted :: status :: "+status);
}
// this is controller initialize function.
function init() {
$scope.testData = null;
// partial URL
$scope.strPartialTestURL = "partials/testView.html;
// send test http request
testService.getTestDataFromServer('testURI', handleSuccessOfAPI, handleFailureOfAPI);
}
init();
}
Now in my jasmine test, we are passing "handleSuccessOfAPI" and "handleFailureOfAPI" method, but these are undefined.
Here is jasmine test code.
describe('Unit Test :: Test Controller', function() {
var scope;
var testController;
var httpBackend;
var testService;
beforeEach( function() {
module('test-angular-angular');
inject(function($httpBackend, _testService_, $controller, $rootScope) {
httpBackend = $httpBackend;
testService= _testService_;
scope = $rootScope.$new();
testController= $controller('testController', { $scope: scope, testService: testService});
});
});
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('Test controller data', function (){
var URL = 'test server url';
// set up some data for the http call to return and test later.
var returnData = { excited: true };
// create expectation
httpBackend.expectGET(URL ).respond(200, returnData);
// make the call.
testService.getTestDataFromServer(URL , handleSuccessOfAPI, handleFailureOfAPI);
$scope.$apply(function() {
$scope.runTest();
});
// flush the backend to "execute" the request to do the expectedGET assertion.
httpBackend.flush();
// check the result.
// (after Angular 1.2.5: be sure to use `toEqual` and not `toBe`
// as the object will be a copy and not the same instance.)
expect(scope.testData ).not.toBe(null);
});
});
I know this is an old case but here is the solution I am using.
Use the 'this' of your controller
.controller('newController',['$scope',function($scope){
var $this = this;
$this.testMe = function(val){
$scope.myVal = parseInt(val)+1;
}
}]);
Here is the test:
describe('newDir', function(){
var svc,
$rootScope,
$scope,
$controller,
ctrl;
beforeEach(function () {
module('myMod');
});
beforeEach(function () {
inject(function ( _$controller_,_$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$compile = _$compile_;
$scope = $rootScope.$new();
ctrl = $controller('newController', {'$rootScope': $rootScope, '$scope': $scope });
});
});
it('testMe inc number', function() {
ctrl.testMe(10)
expect($scope.myVal).toEqual(11);
});
});
Full Code Example
As is you won't have access to those functions. When you define a named JS function it's the same as if you were to say
var handleSuccessOfAPI = function(){};
In which case it would be pretty clear to see that the var is only in the scope within the block and there is no external reference to it from the wrapping controller.
Any function which could be called discretely (and therefore tested) will be available on the $scope of the controller.