I keep running into problems at the minute when i'm testing my AngularJS applications, I try and inject all dependencies however it doesn't seem to be working, any help is greatly appreciated :)
It's quite a large application and i'm trying to break things down as much as possible and test them, however we have a factory called firebaseUser which is, as you can guess a firebaseUser. We also have an instance of this known as userInstance so I'm getting errors whenever I try and mock userInstance.
describe('Dashboard Start Controller', function () {
var scope, ctrl;
beforeEach(function () {
MockFirebase.override();
module('noodleApp.start');
module('noodleApp.noodleFactory');
module('noodleApp.firebaseUser');
module('noodleApp.start');
});
beforeEach(inject(function($rootScope, $controller, $injector) {
scope = $rootScope.$new();
ctrl = $controller('StartController', {$scope: scope});
}));
afterEach(function () {
scope.$destroy();
});
it('should be available', function() {
expect(ctrl).toBeDefined();
});
it('should init with filter being set to all', function() {
expect(scope.filterOn).toBe('all');
});
});
Whenever I run this test I get the following error: Unknown provider: userInstanceProvider <- userInstance <- StartController
$controller is a call to $inject but treated as a service. What happens is you are instantiating the controller but because $inject is used, and you are not passing userInstance, it looks for a provider which isn't found. You need to make sure to pass the service/factory/resolve to your $controller method. By mocking it out and passing it to the controller, you can isolate what you expect to happen and only test the controller in this unit test.
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
userInstanceMock = {
// mock out all methods here
foo: sinon.stub()
}
ctrl = $controller('StartController', {
$scope: scope,
userInstace: userInstaceMock
});
}));
Related
I have a module
export default angular.module('pfui.user', [])
.controller('ModifyUserController', ModifyUserController)
that has a controller
export default class ModifyUserController{
userid:string;
...
}
I'm trying to create a unit test that can test some methods in the controller that calls services to do some operation. This is my Karma script -
describe('ModifyUserControllerTester', function () {
var $controller;
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('pfui.user'));
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('Test', function () {
it('test accessing controller', function () {
let $scope = {};
var controller = $controller('ModifyUserController', {
$scope: $scope
});
expect($scope['userid']).toBe(undefined);
});
});
});
When I run the test, I get an error
Error: [$injector:unpr] Unknown provider: UsersProvider <- Users <- ModifyUserController
Initially I was getting an error that $stateProvider was missing. So I added
beforeEach(angular.mock.module('ui.router'));
and that error went away.
This is my first attempt in writing a Karma test. I'm not sure what I am missing. Why is Karma looking for a Provider when I don't have one in the module? Any help is greatly appreciated.
Your question doesn't show any dependency injections to the ModifyUserController but going by the error you have posted it looks like you haven't provided the 'Users' Service to the controller.
describe('ModifyUserControllerTester', function () {
var $controller;
var mockUsers;
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('pfui.user'));
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('Test', function () {
it('test accessing controller', function () {
//----define your mock dependency here---//
let mockUsers = jasmine.createSpyObj('mockUsers', ['user_method1',
'user_method2',...]);
let $scope = {};
var controller = $controller('ModifyUserController', {
$scope: $scope,
Users: mockUsers
});
expect($scope['userid']).toBe(undefined);
});
});
});
PS. Since its best practice for unit tests to be conducted in isolation, you should also consider providing a mock state provider vs importing the actual ui.router module
Module is defined as
var $manage = angular.module('manage', [....]);
Controller is defined as
$manage.line.events.controller('eventsController', [..., function(...){
$scope.page = "events";
}]);
My simple unit test case is
describe('Module: manage', function() {
beforeEach(module('manage'));
var scope, ctrl, rootScope;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('eventsController', {
$scope: scope
});
}));
it("test page", function () {
expect(scope.page).toEqual('events');
});
});
Here, i am getting a error like
Failed to instantiate module ampleManage due to.... manage is not available.
I have integrated angular-mocks.js also. Tried so many possibilities but not working for me.
Basic need is .
Need to access controller/scope in test case.
We're just getting started with unit testing in our Angular app and we are using a karma/mocha/chai framework for unit testing. I carried out some basic unit tests on various services and factories we have defined and it the unit testing works great. However now we want to test some controller and evaluate the scope vars that the controllers are modifying.
Here is an example of one such controller:
angular.module('App').controller('myCtrl', ['$scope', 'APIProxy',
function ($scope, APIProxy) {
$scope.caseCounts = {caseCount1: 0, caseCount2: 0};
$scope.applyCounts = function () {
$scope.caseCounts.caseCount1 = {...some case count logic...}
$scope.caseCounts.caseCount2 = {...some case count logic...}
};
APIProxy.getAll().then(function (data) {
{...do a bunch of stuff...}
$scope.data = data;
$scope.applyCounts();
});
}]
);
Now, when I unit test I would like to start off with just a simple 'does the $scope.caseCounts have values > 0, then I'll build from there. However it is not obvious how to make the controller cause the APIProxy service run and how to handle the eventual return of data. We've tried $scope.getStatus(), and $scope.apply() and a few other things but I feel like we are way off the mark and we are fundamentally missing something about how to go about this.
Currently our controller tester looks like:
describe("myCtrl unit tests",function(){
beforeEach(module('App'));
var ctrl, APIProxy;
beforeEach(inject(function ($rootScope, $controller, _APIProxy_)
{
$scope = $rootScope.$new();
APIProxy = _APIProxy_;
ctrl = $controller('myCtrl', {$scope: $scope, APIProxy: APIProxy});
}));
it('Loads data correctly', function() {
expect(ctrl).to.not.be.undefined;
//??? what else do we do here to fire the getAll function in controller?
});
});
It's usually better to test the service and the controller separately.
To test the service, you can use $httpBackend to mock the XHR requests:
https://docs.angularjs.org/api/ngMock/service/$httpBackend
To test the controller, you can simply provide mocked values instead of the actual service when you initalize the controller
APIProxy = {'mocked':'data'};
ctrl = $controller('myCtrl', {$scope: $scope, APIProxy: APIProxy});
Or more generally, to mock any provider of your module:
module(function($provide) {
$provide.constant('ab','cd');
$provide.value('ef', 'gh');
$provide.service('myService', function() { });
});
Which will override the 'myService' referenced as dependencies in your controller (if there is one). If you need it directly, you can then inject it too:
var myService;
beforeEach(inject(function (_myService_) {
myService = _myService_;
}));
If you need APIProxy to return a promise, you can mock it too with
https://docs.angularjs.org/api/ng/service/$q
and resolve, e.g.:
var deferred = $q.defer();
deferred.resolve({'mocked':'data'});
return deferred.promise;
If you do want to test them together, you can do a spy on the API function you call and have the spy return a resolved promise.
My code of test_Spec.js
describe('Myctrl', function() {
var $httpBackend, scope, createController, authRequestHandler;
// Set up the module
beforeEach(module('myApp'));
beforeEach(inject(function($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
authRequestHandler = $httpBackend.when('GET', 'http://www.w3schools.com/angular/customers.php')
.respond(true);
// 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('Myctrl', {'$scope' : scope});
};
})
);
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should fetch authentication token', function() {
$httpBackend.expectGET('http://www.w3schools.com/angular/customers.php');
var controller = createController();
expect(scope.names).toBe(true);
$httpBackend.flush();
});
});
This is my test.js service call
var myApp = angular.module('myApp', []);
app.controller('Myctrl', function($Scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.success(function(response) {$Scope.names = response.records;});
});
and these are the errors that I'm getting:
Here is a summary of the errors:
Error: [$injector:unpr] Unknown provider: $rootScopeProvider <- $rootScope <- $httpBackend http://errors.angularjs.org/1.2.0/$injector/unpr?p0=%24rootScopeProvider%20%3C-%20%24rootScope%20%3C-%20%24httpBackend
TypeError: Cannot read property 'expectGET' of undefined
TypeError: Cannot read property 'verifyNoOutstandingExpectation' of undefined
What is the solution for the errors? I have tried several things but I can't figure out the exact solution. Please assist on this.
You have a typo in your dependencies that you want to inject into your controller.
Change $Scope to $scope ;)
As for the error in your test, try injecting the services this way:
beforeEach(inject(function (_$httpBackend_, _$rootScope_, _$controller_) {
…
Well, I know it is too late to answer but in case anyone else stumbles upon similar error, you can try the following:
You never defined the scope variable. You just declared it. So it actually should be:
scope = $injector.get('$rootScope').$new();
Also note the $new(). For every new test case, (as a best practice) you should get a new scope.
I did this controller
app.controller('controller',['$scope','httpServices',function($scope,httpServices){
$scope.items= undefined;
httpServices.getItems( function(items){
$scope.items= items;
});
}]);
and I wrote this test
describe('controller', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('controller', {
'$scope': scope
});
}));
it('defined', function () {
expect(scope.items).toBeUndefined();
})
});
How I can test the scope.items after to have called the service?
I assume that your service httpServices is making some http requests. Therefore you should use the mock-backend service in order to test your controller.
Something like this, pay attention to the comments that I've made inside the code:
describe('Your specs', function() {
var $scope,
$controller,
$httpBackend;
// Load the services's module
beforeEach(module('yourApp'));
beforeEach(inject(function(_$controller_, $rootScope, _$httpBackend_) {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$controller = _$controller_;
//THIS LINE IS VERY IMPORTANT, HERE YOU HAVE TO MOCK THE RESPONSE FROM THE BACKEND
$httpBackend.when('GET', 'http://WHATEVER.COM/API/SOMETHING/').respond({});
var createController = function(){
$controller('controller', {$scope: $scope});
}
}));
describe('Your controller', function() {
it('items should be undefined', function() {
createController();
expect(scope.items).toBeUndefined();
});
it('items should exist after getting the response from the server', function () {
//THIS LINE IS ALSO VERY IMPORTANT, IT EMULATES THE RESPONSE FROM THE SERVER
$httpBackend.flush();
expect(scope.items).toBeDefined();
});
});
});
The question title states this is to test a service, but the code of the question looks like an attempt is being made to test the controller. This answer describes how to test the controller.
If you're testing the controller that calls httpServices.getItems, then you need to mock it/stub getItems in order to
Control it on the test
Not assume any behaviour of the real httpServices.getItems. After all, you're testing the controller, and not the service.
A way to do this is in a beforeEach block (called before the controller is created) provide a fake implementation of getItems that just saves the callback passed to it.
var callback;
beforeEach(inject(function(httpServices) {
callback = null;
spyOn(httpServices, 'getItems').and.callFake(function(_callback_) {
callback = _callback_;
});
});
In the test you can then call this callback, passing in some fake data, and test that this has been set properly on the scope.
it('saves the items passed to the callback on the scope', function () {
var testItems = {};
callback(testItems);
expect($scope.items).toBe(testItems);
});
This can be seen working at http://plnkr.co/edit/Z7N6pZjCS9ojs9PZFD04?p=preview
If you do want to test httpServices.getItems itself, then separate tests are the place for that. Assuming getItems calls $http, then you are most likely to need to use $httpBackend to handle mock responses. Most likely, these tests would not instantiate any controller, and I suspect not need to do anything on any scope.