I'm trying to write tests for some AngularJS code, but can't even get a hello world to run. Suppose my code looks like this:
var myApp = angular.module("myApp", [])
myApp.controller("MyCtrl", function ($scope) {
$scope.hello = "world"
})
Then the angular docs here suggest that something like this (using jasmine) should work:
describe("my controller", function () {
it("should say hello", function () {
var $scope
inject(function ($rootScope, $controller) {
$scope = $rootScope.$new()
$controller('MyCtrl', {$scope: $scope})
})
expect($scope.hello).toBe("world")
}
}
Unfortunately, inject does not exist, and there are no hints in the docs as to where to get it. Thus the approach in the docs doesn't work.
Looking slightly farther afield, we find $injector, which can be created by angular.injector. From those docs, it's fairly clear that inject(f) should be $injector.invoke(f). So we stick this at the top of our code and make the change:
$injector = angular.injector(["myApp"])
Unfortunately, this gives the error "Uncaught Error: Unknown provider: $controllerProvider from myApp", which my google-fu seems unable to elucidate.
I had been using this ($injector) previously when I was only testing services, and it works perfectly. It is only when mixed with a controller definition that I get the error. It can handle the controller definition, or the $injector, but not both. To me this suggests some kind of priority conflict or double-initialization, but I can't figure it out.
So what does the "Unknown provider..." error mean, and how do I get my hello world controller test working? If someone can help sort me out, that would be great.
I created for you an skeleton you could use for that concrete controller.
describe('my controller', function() {
var $scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
$controller('MyCtrl', { $scope: $scope });
}));
it('should contain world', function() {
expect($scope.hello).toBe('world');
});
});
Before each test, you inject your app module and before each test, you creates a new scope and instantiates your controller. Then you just need to write as much tests as you need.
We create a new scope in every tests to have a fresh state. We don't want to modify some $scope in one test and then have another one failing because you modified your $scope earlier.
Plunker: http://plnkr.co/edit/4hHqdsvnVwyUhiezpdhW?p=preview
Write a comment if you have any question.
Related
I created an application using AngularJS which you can only access after logging in.
Now I'm starting to learn how to use Jasmine.
I created a simple test (see below) just to see if everything is ok.
describe('dashboardController', function() {
beforeEach(module('mainModule'));
var $controller;
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('demo', function() {
it('demo spec', function() {
var $scope = {};
var controller = $controller('dashboardController', { $scope: $scope });
expect($scope.foo).toEqual(1);
});
});
});
When I load the application I get "finished in 0.011s1 spec, 0 failures" which means the test went just fine.
However it is blocking me from doing anything. I cant click on the login button or anything else. I also get this error in the console:
"Error: Unexpected request: GET api/getUser
No more request expected
at $httpBackend (angular-mocks.js:1232)"
this route above is responsible for checking if the user is logged in or not.
Im new to Jasmine with AngularJS. Can someone clarify what's going on here?
EDIT: Here's my controller:
angular.module("mainModule")
.controller("dashboardController", ["$scope", "$rootScope", function ($scope, $rootScope) {
$scope.foo = 1;
}]);
I have the following code in my spec file
beforeEach(function () {
module('app');
inject(function ($injector) {
user = $injector.get('app.user');
});
});
user is undefined, and isn't being injected. So I want to make sure that the app module actually loaded.
If the module is not loaded, you get $injector:nomod error. If the module is loaded but the service cannot be found, you get $injector:unpr error. It is as easy as that. There is always a breadcrumb trail, no need to probe Angular to know if it fails silently or not.
Just make sure you're using the right module name. You can use beforeEach to load your module. Also, with $injector you can get an instance of your service or controller you're trying to test:
'use strict';
describe('MyControllerName', function () {
var MyControllerName;
beforeEach(module('myAppMomduleName'));
beforeEach(inject(function ($injector) {
MyControllerName = $injector.get('MyControllerName');
}));
it('should create an instance of the controller', function () {
expect(MyControllerName).toBeDefined();
});
});
I am trying to write some unit tests for an angular project. I've followed some examples about testing a controller but I always end up with the same problem: $rootScope seems to be undefined.
My test.js:
describe("Unit: BodyCtrl", function() {
var scope;
beforeEach(function($rootScope, $controller) {
scope = $rootScope.$new();
});
it('foo should be foo', function() {
expect("foo").toBe("foo");
});
})
and the error:
TypeError: Cannot read property '$new' of undefined'
I have included all the angular files in karma.conf.js. Could it cause problems that I have mounted the main application through sshfs from a server to my local Ubuntu 14.04 and Karma is installed on the said local machine?
You need to inject it. Use inject in beforeEach, otherwise it is a mere variable defined in your function scope which is not defined with any value.
Example:-
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('yourcontroller', {
$scope: $rootScope.$new()
});
}));
I have an angular app that i am developing tests for. In the app.js file i have something like this:
angular.module('app',
[
'app.config',
'app.factories',
'app.directives',
'app.controllers'
]
);
For each controller i want to be in that controller module i essentially define them like this:
angular.module('app.controllers').controller("controller1" ,[],function(){
bleh bleh bleh
code code code
})
The goal here is to write some unit tests with karma but unfortunately the most i have been able to figure out how to do is make sure the dependencies of my main modules load.
What I need to figure out is using the structure i have, how do I (a) create a test to make sure that my controller is actually there, and (b) test things inside the controller
I have tried multiple ways but cannot seem to instantiate the controller within my test framework.
You can test for the existence of your controller like this:
describe("SomeControllerTest", function () {
var scope, ctrl;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('SomeController', {
$scope: scope
});
}));
it("should be defined", function () {
expect(ctrl).toBeDefined();
});
});
Careful with your controller syntax. The second param is an array of strings ending with the function, the function is not a 3rd param.
app.controller('MyController', [ '$log', function MyController($log) {} ]);
I have been writing some Jasmine unit tests in Angular. In the first example I'm testing a controller.
myApp.controller('MyCtrl', function($scope, Config){
...
});
I have a configuration service (Config) that keeps configuration from the database and is injected into my controller. As this is a unit test, I want to mock out that configuration service altogether, rather than allowing execution to pass through it and using $httpBackend. Examples I found taught me about a $controller function I can use like this, in order to get an instance of my controller with my mocks injected in place of the usual collaborator:
beforeEach(inject(function($controller, $rootScope){
var scope = $rootScope.$new();
var configMock = {
theOnlyPropertyMyControllerNeeds: 'value'
};
ctrl = $controller('MyCtrl', {
$scope:scope,
Config: configMock
});
}));
But I also have other services that use the Config service. To help unit test them, I assumed there would be a similar $service function I could use to instantiate a service with whatever mocks I want to provide. There isn't. I tried $injector.get, but it doesn't seem to let me pass in my mocks. After searching for a while, the best I could come up with in order to instantiate a service in isolation (avoid instantiating its collaborators) is this:
beforeEach(function() {
mockConfig = {
thePropertyMyServiceUses: 'value'
};
module(function($provide) {
$provide.value('Config', mockConfig);
});
inject(function($injector) {
myService = $injector.get('MyService');
});
});
Is this the right way? It seems to be overriding the entire application's definition of the Config service, which seems maybe like overkill.
Is it the only way? Why is there no $service helper method?
For unit testing, it is common that you override a service for the sake of testing. However, you can use $provide to override an existing service instead of using inject, as long as you load the application before hand.
Assuming that you created Config using something like:
angular.moduel('...', [...]).factory('Config', function (...) {...});
If so, try this:
...
beforeEach(module("<Name of you App>"));
beforeEach(
module(function ($provide) {
$provide.factory('Config', function (...) {...});
});
);
...
After that, when you initialise your controller, it will get the mocked Config.