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});
});
Related
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();
}));
});
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();
}));
});
});
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.
I am trying to figure out how to write some simple Karma tests, but am having some trouble with syntax and just understanding the framework. Here is a controller that I want to write a test for:
myApp.controller('menuController', ['$rootScope', '$parse', '$attrs', '$scope', 'Menu', 'Track', '$state',
function($rootScope, $parse, $attrs, $scope, Menu, Track, $state){
$scope.Menu = Menu;
$rootScope.$on("click",function() {
Menu.active = false;
});
$scope.click = function(button) {
Track.event(2, button + "_button_pressed", true);
};
}])
Here is a simple test I wrote for this controller, I am trying to test for the menu button's active status changing when it is clicked:
describe('Controller: menuController', function() {
//load the module with menuController
beforeEach(module('myApp'));
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('menuController', {
$scope: scope
});
}));
//put test here
it ('should change $scope.Menu.active to true when button is clicked', function() {
expect(scope.Menu.active).toBeFalse();
scope.click();
expect(scope.Menu.active).toBeTrue();
})
});
After running Grunt test, the test fails and one of the errors it returns is:
TypeError: 'undefined' is not an object (evaluating 'scope.Menu')
I was hoping if anyone can tell me what I'm doing wrong or show me how to write a simple karma test for the controller shown above.
Thanks in advance!
so I'm new to angularjs and its mocking library. I am trying to test that a specific GET request is made, but I always get this error for the 2nd assertion and can't figure out why:
Error: Unsatisfied requests: GET /1.json
Is there anything I messed up with my code below?
App.js
var App = angular.module('App', []).config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
}).when('/Items', {
templateUrl: 'views/items.html',
controller: 'Ctrl'
}).otherwise({
redirectTo: '/'
});
}]);
Ctrl.js
function Ctrl($scope, $http, $filter) {
$scope.items = [];
$http.get('/1.json').success(function(data) {$scope.items = data.items;});
}
Ctrl.$inject = ["$scope","$http", "$filter"];
Spec/Ctrl.js
describe('Controller: Ctrl', function() {
var $httpBackend;
// load the controller's module
beforeEach(module('App'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
$httpBackend.whenGET('/1.json').respond('Response!');
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
var Ctrl, scope;
// Initialize the controller and a mock scope
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
Ctrl = $controller('Ctrl', {
$scope: scope
});
}));
it('should initialize with 0 items', function() {
expect(scope.items.length).toBe(0);
$httpBackend.flush();
});
it('should make store request', function(){
var controller = scope.$new(Ctrl);
$httpBackend.expectGET('/1.json');
$httpBackend.flush();
});
});
EDIT: added app and controller code.
I finally got my unit tests working! Mostly because I restructured my application to make more sense and be more modular.
I'll try to give information to help the next person that runs into this:
first of was I switched to using the $resource instead of $http.
instead of injecting $injector, I injected $httpBackend like so:
beforeEach(inject(function(_$httpBackend_, $rootScope, $route, $controller){
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/path/to/api').respond([{id:1}]);
instead of referencing 'Ctrl' as a string, I passed in the actual class
Ctrl = $controller('Ctrl', {
$scope: scope
});
became
var ProductsCtrl = ['$scope', function($scope){ ... }];
Ctrl = $controller(ProductsCtrl, {
$scope: scope
});`
Make sure you are referencing the angular-resources.js file if you are using $resources
I'm really loving Angularjs; I think it just takes some time to wrap your head around how to test. Best of luck out there!