I'm trying to set up unit tests for an existing Angular JS project, but I keep getting the error in the title:
Unknown provider: $$qProvider <- $$q <- $interval
Here is my unit test:
describe("screen controller", function(){
beforeEach(module('tsApp'));
var scope, createController, $interval, $timeout, $translate, $sce, $controller;
beforeEach(inject(function(_$controller_, $rootScope, _$interval_, _$timeout_, _$translate_, _$sce_ ){
// The injector unwraps the underscores (_) from around the parameter names when matching
$interval = _$interval_;
$timeout = _$timeout_;
$translate = _$translate_;
$sce = _$sce_;
$controller = _$controller_;
scope = $rootScope.$new();
createController = function() {
return $controller('screenCtrl', {
'$scope' : scope,
'$interval' : $interval,
'$timeout' : $timeout,
'$translate' : $translate,
'$sce' : $sce
});
};
}));
describe('first test', function() {
it('it runs without error!', function() {
var controller = createController();
expect(true).toEqual(true);
});
});
});
And the controller I'm trying to test starts like this:
var screenCtrl = tsApp.controller('screenCtrl', function($scope, updateService, $translate, $sce, $interval, $timeout) {
I'm guessing there is something wrong with the dependancies I'm injecting. Thanks in advance.
$$qProvider was introduced in AngularJS 1.3.0-beta.14. It is undocumented and used internally.
Prior to this version $IntervalProvider used $q and in beta.14 and later it uses both $q and $$q.
Somewhere you have conflicting versions of AngularJS modules.
Check all your files or for example your Bower components.
Make sure your core AngularJS version is high enough for other modules you might be using. Angular Material for example requires Angular 1.3.x.
Related
Hi I've been trying to unit test basic functions in my controller however I can't seem to connect when setting up the unit test.
Error: [$injector:modulerr] Failed to instantiate module myApp due to:
[$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Here is my controller:
var myApp = angular.module("myApp", []);
myApp.controller('studentController', function($scope,$route,$routeParams,$http,$location){
//Get all students
$scope.getStudents = function(){
$http.get('/api/student/').then(function(response){
$scope.students = response.data;
});
};
and my test:
describe("studentController", function () {
beforeEach(module('myApp'));
var $controller;
beforeEach(inject(function (_$controller_){
$controller = _$controller_;
}))
describe("studentController", function(){
it("should get student data", function (){
var $scope = {};
$scope.getStudents();
expect($scope.students.length).toBe(15)
})
})
});
I have included both these files in the jasmine html file along with the angular-mocks.js
any help would be much appreciated
You are injecting $route, but you are not loading the ngRoute module. Load the file angular-route.js and state the dependency:
var myApp = angular.module("myApp", ['ngRoute']);
You have to create controller in before each by using following and as you are calling getStudents that shuold be mocked by using HttpBackend service in unit test.
Controller
var myApp = angular.module("myApp", []);
myApp.controller('studentController', function($scope, $route, $routeParams, $http, $location) {
//Get all students
$scope.getStudents = function() {
$http.get('/api/student/').then(function(response) {
$scope.students = response.data;
});
};
});
Test file
describe("studentController", function() {
beforeEach(module('myApp'));
var $controller, scope, route;
beforeEach(inject(function(_$controller_, $rootScope, $route, $routeParams, $http, $location) {
$controller = _$controller_;
scope = $rootScope.$new();
$controller('studentController', {
'$scope': scope,
'$route': $route,
'$routeParams': $routeParams,
'$http': $http,
'$location': $location
});
}))
describe("studentController", function() {
it("should get student data", function() {
// for this you have to use httpBackend
// you have to mock the response of api
$scope.getStudents();
// then you are able to verify the result in student
expect($scope.students.length).toBe(15)
})
})
});
For more information you can refer unit testing and Httpbackend
I am trying to implement unit testing for a particular controller of a web app using Jasmine and Karma. At the moment it is giving the following error:
Chrome 53.0.2785 (Mac OS X 10.10.5) HomeCtrl should be defined FAILED
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope
http://errors.angularjs.org/1.5.5/$injector/unpr?p0=%24scopeProvider%20%3C-%20%24scope
Here is the code of the testing file:
describe('HomeCtrl', function(){
var $controller, HomeCtrl;
beforeEach(angular.mock.module('TestModule'));
beforeEach(inject(function(_$controller_, _$rootScope_, _$scope_) {
$controller = _$controller_;
rootScope = $rootScope.new();
scope = $scope.new();
HomeController = $controller('HomeCtrl', {
$scope: scope
});
}));
// Verify our controller exists
it('should be defined', function() {
expect(HomeController).toBeDefined();
});
});
Could someone tell me what I am doing wrong?
In AngularJS, all scopes are children of $rootScope.
In unit tests, you cannot inject $scope since, there is no service that exists. But there is an $rootScope provider which contains API like $new to create a new child scope.
$rootScope.$new(), create a new child scope.
Since, you cannot inject $scope you are getting (no such provider exists to provide you $scope)
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope
In the answer, you have provided,
beforeEach(inject(function(_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
HomeCtrl = $controller('HomeCtrl', {
$scope: $rootScope,
});
}));
You are injecting $rootScope and directly passing the $rootScope to HomeCtrl. It works, but while executing tests it adds all variables and function in controller code to $rootScope.
But in real scenario, your HomeCtrl is expecting a $scope(child scope). So, to replicate the actual scenario, it would be better if you pass a child scope.
beforeEach(inject(function(_$controller_, _$rootScope_) {
$controller = _$controller_;
$scope = _$rootScope_.$new();
HomeCtrl = $controller('HomeCtrl', {
$scope: $scope,
});
}));
The following code succeeds:
describe('HomeCtrl', function(){
var $controller, HomeCtrl;
var $rootScope, $scope;
beforeEach(angular.mock.module('TestModule'));
beforeEach(inject(function(_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
HomeCtrl = $controller('HomeCtrl', {
$scope: $rootScope,
});
}));
// Verify our controller exists
it('should be defined', inject(function($controller) {
expect(HomeCtrl).toBeDefined();
}));
});
I'm starting to learn how to do unitTesting with jasmine. I read a lot of in internet and SO but I can't solve my problem.
I have a directive which has a controller. That controller is using the service $uibModal to open a modal when I click an element. I'm trying to inject that service from my test but I can't. I read a lot of threads saying that I must pass an instance. I'm trying to do so but I can't. Please any help will be appreciated.
.controller('myController', ['$scope', '$uibModal', function($scope, $uibModal){
var self = this;
//OTHER CODE
self.openMyModal = function(dataInput) {
var modalInstance = $uibModal.open({
animation: true,
bindToController: true,
templateUrl: 'app/myComponent/modals/component-modal.html',
controllerAs: 'componentModalCtrl',
controller: 'componentModalController',
windowClass: 'semi-modal semi-modal--large',
scope: $scope
})
}
//OTHER CODE
}
This is the test where I'm trying to mock this modal.
beforeEach(function(){
angular.mock.module('templates');
angular.mock.module('app.components.myComponent');
angular.mock.inject(function($compile, $rootScope, $templateCache, $controller){
scope = $rootScope;
modalInstance = { close: function(){}, dismiss: function(){}, open: function(){}
};
//Initializing element and doing compile and digest
controller = $controller('myController', {$scope: scope, $uibModal: modalInstance});
})
I'm getting the error
Unknown provider: $uibModalProvider <- $uibModal.
Can I inject this service in another way? What I'm doing wrong?
P.S: I have read this Testing AngularUI Bootstrap modal instance controller
Angular ui bootstrap $uibModalInstance breaks down unit tests
Mocking $modal in AngularJS unit tests
Try this one:
beforeEach(module(function ($provide) {
$provide.service("$uibModal", function () {
// mock methods here
});
}));
Finally I solved it. It was a silly error. I imported an additional module that I was missing in my tests. After that I could mock my service and use it without any problem like this.
angular.mock.inject(function($compile, $rootScope, $templateCache, $controller, $uibModal){
scope = $rootScope;
uibModal = $uibModal;
element = angular.element('<directive-tree input-tree=inputTree subsystem=subsystem></directive-tree>');
$compile(element)(scope);
scope.$digest();
controller = $controller('directiveTreeController', {$scope: scope, $uibModal: uibModal});
});
I am trying to test my d3 and angular app using Jasmine and Karma task runner. It works when I use the app in the browser but I am having problems setting up the tests. I am also using browserify.
The factory service loads in the d3 dependency (instead of putting d3 in a script tag) which is used by the world map service, this controller and the directive (below):
.factory('d3Service', ['$document', '$q', '$rootScope', '$window', d3Service])
.service('Category', ['$http', categoryService])
//most of the d3 methods are in this service
.service('WorldMap', ['d3Service', worldMapService])
.controller('MapCtrl', ['$scope', 'd3Service', 'Category', '$http',
function($scope, d3Service, Category, $http) {
// waits until d3 is loaded then gets the world
// data json file and set to controller's scope
d3Service.d3().then(function(d3){
$http.get("world.json").success(function(world) {
$scope.countries = topojson.feature(world, world.objects.countries).features;
});
});
}
])
//the directive is what contains the d3 map
.directive('wmMap', ['d3Service', 'Category', '$window', 'ngDialog', 'WorldMap', wmMap]);
The directive similarly waits for the d3 dependency to load:
var wmMap = function(d3Service, Category, $window, ngDialog, WorldMap){
return {
restrict: 'EA',
link: function(scope, ele, attrs){
d3Service.d3().then(function(d3) {
//do some stuff
// when world data json is loaded and scope is set
// call render to set map on page
scope.$watch('countries', function(countries){
if(countries !== undefined){
WorldMap.render(ele[0], zoom, countries, Category, ngDialog);
}
});
});
}
}
}
Tests - use $httpBackend.expectGet() to set some data and then $httpBackend.flush() to load it to the test in the 'it' block. The $scope data that should be loaded in for $scope.countries is not there...?
describe('d3', function(){
var data, $q, $rootScope, $compile, $window, $httpBackend, html, element;
beforeEach(function(){
mockd3Service = {};
mockMapService = {};
module('WorldMaps');
//provide services
module(function($provide){
$provide.value('d3Service', mockd3Service);
$provide.value('WorldMap', mockMapService);
});
inject(function($injector,_$compile_, _$rootScope_, _$window_, _$q_, _$controller_, _$httpBackend_){
$window = _$window_;
$compile = _$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_;
$q = _$q_;
$httpBackend = _$httpBackend_;
// load in some mock data for http request
$httpBackend.expectGET('world.json')
.respond({arcs: ['abc'],
objects: {countries: {geometries: [{arcs:[], id: "Netherlands", type: "Polygon"}]}},
transform: {scale: [], translate: []},
type: "Topology"}
);
$scope = $rootScope.$new();
});
mockd3Service.d3 = function(){
var deferred = $q.defer();
deferred.resolve($window.d3);
return deferred.promise;
}
});
it('created', function(){
//check d3 service is running
html = '<wm-map></wm-map>';
element = angular.element(html);
element = $compile(html)($rootScope);
$rootScope.$digest();
expect($scope.countries).toBeUndefined();
ctrl = $controller('MapCtrl', {'$scope' : $scope});
$httpBackend.flush();
//$scope is logged out with a countries property
//but countries is undefined
console.log($scope);
});
})
You could get your JSON file with $http instead and mock that with $httpBackend, then pass the JSON data to D3.
Of course, if you are trying to test D3 rendering, this approach doesn't solve that, but if all you want to do is ensure that the HTTP request was made, you could take that approach instead.
Using a fresh clone of angular-seed I am attempting some BDD and have added the following tests and code. However, once I add the $scope to the controller, the suite fails on the expect(view1Ctrl).toBeDefined(); expectation.
Below is the only addition I've made and it causes the noted failure when Karma runs.
app/view1/view1.js
.controller('View1Ctrl', ['$scope', function($scope) {
$scope.name = "Name";
}]);
in your test (view1_test.js) you need to inject $scope into the controller...
describe('myApp.view1 module', function() {
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
it('should ....', inject(function($controller, $rootScope) {
//spec body
var $scope = $rootScope.$new();
var view1Ctrl = $controller('View1Ctrl', {$scope: $scope});
expect(view1Ctrl).toBeDefined();
}));
});
});