Using Angular 1.5.6 and angular-mocks#1.5.6
Here is a stripped down version of my spec file:
describe('Controller: blahCtrl', function() {
var blahCtrl;
var rootScope;
var scope;
beforeEach(module('blahModule'));
beforeEach(inject(function($controller, $rootScope) {
console.log('inject beforeEach is hit'); // does not log
rootScope = $rootScope;
scope = $rootScope.$new();
blahCtrl = $controller('blahCtrl as vm', {
$scope: scope,
$rootScope: rootScope
});
}));
describe('blahMethod()', function() {
beforeEach(function() {
console.log('describe beforeEach is hit'); // logs
});
it('does something', function() {
console.log('describe it block is hit'); // logs
});
});
});
What could be the reason why my inject beforeEach is never hit? Surely this should be fine?
Related
I tried to inject a factory to my controller in jasmine test like in
Unit testing AngularJS factories that have dependencies
When I $provide a factory in the test, I would expect the controller to use the provided factory. But the console.log still prints 'real value'. I don't get it.
var app = angular.module('mod', []);
app.factory('factoryA', [
function () {
return "real value";
}
]);
app.controller('myController', ['factoryA',
function (factoryA) {
console.log(factoryA);
}
]);
describe("test", function() {
var $scope, $controller, $httpBackend;
var app;
beforeEach(function() {
module(function($provide) {
$provide.factory('factoryA', function () { return "fake value"; });
});
app = module("mod");
inject(function (_$controller_, _$httpBackend_, $rootScope) {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$controller = _$controller_;
});
});
it("works", function() {
$controller("myController", { '$scope': $scope });
});
});
You would need to get the $provider from the module mod so pass module name as first argument so that it overrides the factoryA definition that was created originally. Or load the module - module("mod") - before setting up mock.
module('mod', function($provide) {
$provide.factory('factoryA', function () { return "fake value"; });
});
Another way it to create mocks and pass it to the controller creation.
describe("test", function() {
var $scope, $controller, $httpBackend;
var app, factoryA;
beforeEach(function() {
module(mod);
inject(function (_$controller_, _$httpBackend_, $rootScope) {
//...Your code
//Mock factory
factoryA = jasmine.createSpy('factoryA');
factoryA.and.returnValue("fake value");
});
});
it("works", function() {
//Pass the mock factory
$controller("myController", { '$scope': $scope, factoryA:factoryA });
});
});
I am using karma via gulp-karma but getting the following error:
Error: [ng:areq] Argument 'adminPagesCtrl' is not a function, got
undefined
Here is my spec file:
describe('adminPagesCtrl', function() {
var $rootScope,
controller;
beforeEach(function() {
angular.module('jhApp')
.config(['$locationProvider',
function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
});
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
controller('adminPagesCtrl', {$scope: scope});
}));
it('does a thing', function() {
expect(true).toBe(true);
});
});
I have checked the browser window that karma opens up and can see all the files loaded. Application is working fine. Not sure what else to try?
This is now working:
describe('adminPagesCtrl', function() {
var $rootScope,
controller;
beforeEach(function() {
module('jhApp')
});
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
controller('adminPagesCtrl', {$scope: scope});
}));
it('does a thing', function() {
expect(true).toBe(true);
});
});
I am trying to write unit tests for the AngularJS application. Below is a pretty standard test template, which works fine:
describe('Controller: MainCtrl', function () {
var MainCtrl, scope;
beforeEach(function() { // <-- what if I remove this
module('watcomApp');
inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope,
});
});
}); // <-- and this
it('some spec', function () {
expect(scope.data).toEqual('something');
});
});
However if I try to reuse the current state of controller and remove beforeEach:
describe('Controller: MainCtrl', function () {
var MainCtrl, scope;
module('watcomApp');
inject(function ($controller, $rootScope) {
...
it stops working because scope.data becomes undefined.
The question is: what happens to scope? I expect it to persist between specs since it's global.
This is my first time testing using Jasmine. I'm having trouble accessing the $scope variables in the spec. I have a failing test:
mysite ProductsDetailCtrl sets hey
Expected undefined to be 1.
Error: Expected undefined to be 1.
spec:
//= require helpers/load-angular-mysite-module
//= require products/controllers/products_detail_controller
describe('mysite', function() {
var $rootScope, $scope, $controller;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function(_$rootScope_, _$controller_) {
$rootScope = _$rootScope_; // don't really
$scope = $rootScope.$new(); // understand what's
$controller = _$controller_; // going on in this function
controller = $controller('ProductsDetailCtrl', {
'$rootScope': $rootScope,
'$scope': $scope
});
}));
it('sets hey', function() {
expect($rootScope.hey).toBe(1);
});
});
});
controller:
app.controller('ProductsDetailCtrl', ['$scope', '$resource', function($scope, $resource) {
$scope.hey = 1;
....
Could someone explain to me how I would access the scope?
You just have to check for the property heyin your $scope not in the $rootScope:
describe('mysite', function() {
var scope, ProductsDetailCtrl;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function($controller, $rootScope) {
// Create a mock scope for your controller.
scope = $rootScope.$new();
// Initialize the controller with the mocked scope.
ProductsDetailCtrl = $controller('ProductsDetailCtrl', {
$scope: scope
});
}));
it('sets hey', function() {
expect(scope.hey).toBe(1);
});
});
});
I am unit testing a controller and I want to test an event handler. Say my controller looks like:
myModule.controller('MasterController', ['$scope', function($scope){
$scope.$on('$locationChangeSuccess', function() {
$scope.success = true;
});
}]);
Would I broadcast that in my Jasmine test? Would I emit it? Is there an accepted standard?
The solution I came up with is as follows:
describe('MasterController', function() {
var $scope, $rootScope, controller, CreateTarget;
beforeEach(function() {
inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
var $controller = $injector.get('$controller');
CreateTarget = function() {
$controller('MasterController', {$scope: $scope});
}
});
});
describe('$locationChangeSuccess', function() {
it('should set $scope.success to true', function() {
controller = CreateTarget();
$rootScope.$broadcast('$locationChangeSuccess');
expect($scope.success).toBe(true);
});
});
});
I don't think there is "an accepted standard" but according to $location source code the event is broadcasted, so I would mock this behavior and test it this way:
'use strict';
describe('MasterController', function() {
var MasterController,
$rootScope,
$scope;
beforeEach(module('myModule'));
beforeEach(inject(function($rootScope, $injector, $controller) {
$rootScope = $rootScope;
$scope = $rootScope.$new();
MasterController = $controller('MasterController', {
'$scope': $scope
});
$scope.$digest();
}));
describe('$locationChangeSuccess event listener', function() {
it('should set $scope.success to true', function() {
var newUrl = 'http://foourl.com';
var oldUrl = 'http://barurl.com'
$scope.$apply(function() {
$rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl);
});
expect($scope.success).toBe(true);
});
});
});