So I have my blank tests passing with this setup.
describe('loginController', function() {
var scope, createController;
beforeEach(module('souply'));
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function() {
return $controller('loginController', {
'$scope': scope
});
};
}));
And here are the tests...
describe('processGoogleLogin', function(){
describe('successful', function(){
beforeEach(function() {
});
it('should connect to google plus', function () {
expect(true).toBe(true);
});
This one passes no problem.
QUESTION: How can I test a method on the login controller?
Here is the method I want to test on the login controller:
$scope.processGoogleLogin = function(){
console.log('process login was clicked');
window.location.replace('/#/dashboard');
};
The test I have so far is:
it('should sign you into the dashboard', function () {
scope.processGoogleLogin();
//$controller.processGoogleLogin();
//expect(window.location).toBe('/#/dashboard');
});
This test throws an error of:
'undefined' is not a function (evaluating 'scope.processGoogleLogin()')
This needed this line.
var ctrl = $controllerConstructor('myController', {$scope: scope, myResolve: {}, state: state});
Related
I am working to initiate a controller sits inside a directive. I have some tests I need to run but right now I am not able to access the controller with ng-Mock.
describe('hero Directive', function () {
var $compile,
$rootScope,
$scope,
element,
ctrl;
beforeEach(function () {
angular.mock.module('ha.module.core');
angular.mock.inject(function (_$compile_, _$rootScope_, _$controller_, $templateCache) {
$compile = _$compile_;
element = angular.element("<div exlore-hereo></div");
$compile(element)($rootScope);
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
ctrl = _$controller_('ExploreHeroController', { $scope: $scope });
console.log(ctrl)
$scope.$digest();
});
});
afterEach(function () {
// need to remove the element element.remove();
});
describe('directive controller', function () {
it('should dispatch call $emit with $methodsBound', function () {
//spyOn($scope, '$emit');
spyOn($scope, 'ControllerName');
//expect(scope.$emit).toHaveBeenCalledWith('$methodsBound');
//expect(ctrl).toHaveBeenCalled();
});
});
});
I created an element compiled it and called the $digest method.
The error that I got was
Argument 'scope' is required.
So I tried spying on the it with jasmine
spyON($scope, 'ControllerName');
My controller inside of my directive is pretty basic.
var ControllerName = function($scope) {
$scope.$emit('$method');
}
It seems like I need a spy, but I am not sure why the one I created does not work.
You can try to spy on $scope, but note that ControllerName is not a member of the $scope object.
However, $emit is....
The thing is, that you call $emit in the controllers constructos, therefore you have to spy on it before:
beforeEach(function () {
...
$scope = $rootScope.$new();
spyOn($scope, '$emit');
ctrl = _$controller_('ExploreHeroController', { $scope: $scope });
...
});
describe('directive controller', function () {
it('should dispatch call $emit with $methodsBound', function () {
expect($scope.$emit).toHaveBeenCalledWith('$methodsBound');
});
});
I am trying to test my modal in my script. but getting an error like
Unexpected request: GET modal/view.html. Here are my codes:
describe('test controller', function () {
var myCtrl, scope, rootScope, $httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function (_$controller_, _$httpBackend_, _$rootScope_) {
rootScope = _$rootScope_;
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_:
myCtrl = _$controller_('myCtrl', {
$scope: scope
});
}));
describe('test', function() {
it('it should load modal' , function() {
scope.loadModal();
//some http request tests
$httpBackend.flush();
});
});
});
controller
$scope.loadModal = function() {
//some http request codes…
$scope.modal = $modal({
template: 'modal/view.html',
scope: $scope
});
};
Getting error: Unexpected request: GET modal/view.html
modal/view.html is from the template params inside the modal
I am not sure how to patch this error in my case. Can anyone help me about it? Thanks a lot!
I have a basic controller that uses $stateParams.
angular.module('example')
.controller('SampleCtrl', ['$stateParams',
function($stateParams) {
var vm = this;
vm.isSomething = ($stateParams.isSomething === 'true') ? true : false;
}
]);
In my unit test, I need to set the $stateParams.isSomething to true in one test, and false in another test.
describe('SampleCtrl Test', function() {
var ctrl, scope, $stateParams;
// set default stateParams
$stateParams = { isSomething: 'false' };
beforeEach(function(){
inject(function($rootScope, $controller){
scope = $rootScope.$new();
ctrl = $controller('SampleCtrl', {
$scope: scope,
$stateParams: $stateParams
});
});
});
describe('when isSomething is false', function() {
it('should be false', function() {
expect(ctrl.isSomething).toBe(false);
});
});
describe('when isSomething is true', function() {
beforeEach(function() {
$stateParams.isSomething = 'true';
});
it('should be true', function() {
// THIS IS FAILING
expect(ctrl.isSomething).toBe(true);
});
});
});
How can I properly mock different states of $stateParams for different tests?
I think you would need to instantiate the controller again with the updated scope object.
You also have a few naming issues as well, see the comments in the code below.
DEMO
describe('SampleCtrl Test', function() {
var ctrl, scope, $stateParams, $controller;
// set default stateParams
// you have called it 'something' in your controller not 'isSomething'
$stateParams = { something: 'false' };
beforeEach(function(){
// load module
module('example');
inject(function($rootScope, _$controller_){
scope = $rootScope.$new();
// angular removes the _'s for you so you can call it $controller
$controller = _$controller_;
ctrl = $controller('SampleCtrl', {
$scope: scope,
$stateParams: $stateParams
});
});
});
describe('when isSomething is false', function() {
it('should be false', function() {
expect(ctrl.isSomething).toBe(false);
});
});
describe('when isSomething is true', function() {
beforeEach(function() {
// you have called it 'something' in your controller not 'isSomething'
$stateParams.something = 'true';
// instantiate a new controller with the updated $stateParams object
ctrl = $controller('SampleCtrl', {
$scope: scope,
$stateParams: $stateParams
});
});
it('should be true', function() {
// THIS IS FAILING
expect(ctrl.isSomething).toBe(true);
});
});
});
The issue you are having is that the beforeEach of the describe('when isSomething is true') evaluates after the beforeEach of the describe('SampleCtrl Test'), i.e. the controller is already instantiated when the $stateParams values are changed. As Matt wrote in his answer you need to instantiate the controller after the change has taken place.
I usually use a simple workaround to this. Since the beforeEach is instantiated after the controller, I usually write an empty it-statement wherein the incorrect stateParams were sent to the controller and ignore that one. For all preceeding it-statements the change to stateParams changes will have taken place and the controller will then use the correct variables:
describe('when isSomething is true', function() {
beforeEach(function() {
$stateParams.isSomething = 'true';
});
it('should take one iteration to set the state parameter', function() {
// do nothing as the controller will not use the new $stateParams yet
});
it('should be true', function() {
// THIS IS NO LONGER FAILING
expect(ctrl.isSomething).toBe(true);
});
});
While not the most beautiful solution it seems to be the simplest
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 have this simple controller, UserService is a service which return JSON
"use strict";
angular.module("controllers").controller('profileCtrl', ["$scope", "UserService",
function ($scope, UserService) {
$scope.current_user = UserService.details(0);
}
]);
I can not make the test. However this is my try
'use strict';
describe('profileCtrl', function () {
var scope, ctrl;
beforeEach(angular.mock.module('controllers'), function($provide){
$provide.value("UserService", {
details: function(num) { return "sdfsdf"; }
});
});
it('should have a LoginCtrl controller', function() {
expect(controllers.profileCtrl).toBeDefined();
});
beforeEach(angular.mock.inject(function($rootScope, $controller){
scope = $rootScope.$new();
$controller('profileCtrl', {$scope: scope});
}));
it('should fetch list of users', function(){
expect(controllers.scope.current_user.length).toBe(6);
expect(controllers.scope.current_user).toBe('sdfsdf');
});
});
The usage of $controller is correct, that's the way to instantiate a controller for a unit test. You can mock the UserService instance it gets directly in the $controller invocation.
You should be using its return value - this is the instance of your controller you're going to test.
You're trying to read stuff from controllers but its not defined anywhere in the test, I guess you're referring to the module.
This is how I would go about it + fiddle
//--- CODE --------------------------
angular.module('controllers', []).controller('profileCtrl', ["$scope", "UserService",
function ($scope, UserService) {
$scope.current_user = UserService.details(0);
}]);
// --- SPECS -------------------------
describe('profileCtrl', function () {
var scope, ctrl, userServiceMock;
beforeEach(function () {
userServiceMock = jasmine.createSpyObj('UserService', ['details']);
userServiceMock.details.andReturn('sdfsdf');
angular.mock.module('controllers');
angular.mock.inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('profileCtrl', {
$scope: scope,
UserService: userServiceMock
});
});
});
it('should have a LoginCtrl controller', function () {
expect(ctrl).toBeDefined();
});
it('should fetch list of users', function () {
expect(scope.current_user).toBe('sdfsdf');
});
});
You're welcome to change the fiddle online to see how it affects testing results.