I need to test below code in angularjs using mocha chai and sinon
$scope.send = function() {
$state.transitionTo('module.sendhome');
};
Below is test case for the same
it('send' , function () {
scope.send();
});
on running the above test case getting error as given below.
Error: No such state 'module.sendhome'
In my test case need to check if $state.transitionTo is called with parameter module.sendhome.
You need to stub out $state and the transitionTo method and write expectations on that. This will keep your unit test clean and flexible, so as to not trigger the real implementation of $state.transitionTo (which in turn triggers the error you are experiencing).
var $scope, $state;
beforeEach(function () {
$state = {};
module('your_module', function ($provide) {
$provide.value('$state', $state);
});
inject(function ($injector, $controller) {
$state = $injector.get('$state');
$scope = $injector.get('$rootScope').$new();
$controller('your_controller', {
$scope: $scope,
$state: $state
});
});
// Stub API
$state.transitionTo = sinon.stub();
});
it('calls the transitionTo method', function () {
$scope.send();
expect($state.transitionTo).to
.have.been.calledOnce
.and.calledWith('module.sendhome');
});
Edit
As per the notion of not stubbing out things we do not own (which, I don't fully agree on but for the sake of argument let's say I do).
Don't stub $state.transitionTo, but rather spy on it.
Now - you will have to register a state matching that of your expectation in order for $state.transitionTo to not crash.
var stateProvider;
beforeEach(function () {
module('ui.router', function ($stateProvider) {
stateProvider = $stateProvider;
});
/** The rest of your beforeEach block **/
stateProvider.state('module.sendhome', {});
});
And then in your it:
it('calls the transitionTo method with the correct params', function () {
var spy = sinon.spy($state, 'transitionTo');
$scope.send();
expect(spy).to
.have.been.calledOnce
.and.calledWith('module.sendhome');
});
Edit#2
If you want to ensure that you ended up on the correct state after invoking your $scope method, I would look into this awesomely awesome stateMock.
Inject stateMock as another module prior to your own and write expectations such as:
afterEach(function () {
$state.ensureAllTransitionsHappened();
});
it('should travel to the correct state', function () {
$state.expectTransitionTo('module.sendhome');
$scope.send();
});
Related
I'm new to jasmine testing. How can I test function call in watch function?
Below is my code. I'm confused about usage of spy in jasmine and how can I handle function call inside watcher.
Do I need to pause fetch() inside watch. Please suggest how to improve my testing skills.
var app = angular.module('instantsearch',[]);
app.controller('instantSearchCtrl',function($scope,$http,$sce){
$scope.$sce=$sce;
$scope.$watch('search', function() {
fetch();
});
$scope.search = "How create an array";
var result = {};
function fetch() {
$http.get("https://api.stackexchange.com/2.2/search?page=1&pagesize=10&order=desc&sort=activity&intitle="+$scope.search+"&site=stackoverflow&filter=!4*Zo7ZC5C2H6BJxWq&key=DIoPmtUvEkXKjWdZB*d1nw((")
.then(function(response) {
$scope.items = response.data.items;
$scope.answers={};
angular.forEach($scope.items, function(value, key) {
var ques = value.question_id;
$http.get("https://api.stackexchange.com/2.2/questions/"+value.question_id+"/answers?page=1&pagesize=10&order=desc&sort=activity&intitle="+$scope.search+"&site=stackoverflow&filter=!9YdnSMKKT&key=DIoPmtUvEkXKjWdZB*d1nw((").then(function(response2) {
$scope.answers[ques]=response2.data.items;
//console.log(JSON.stringify($scope.answers));
});
});
});
}
});
my test case:
describe('instantSearchCtrl', function() {
beforeEach(module('instantsearch'));
var $scope, ctrl;
beforeEach( inject(function($rootScope, $controller) {
// create a scope object for us to use.
$scope = $rootScope.$new();
ctrl = $controller('instantSearchCtrl', {
$scope: $scope
});
}));
/*var $scope = {};
var controller = $controller('instantSearchCtrl', { $scope: $scope });
expect($scope.search).toEqual('How create an array');
//expect($scope.strength).toEqual('strong');*/
it('should update baz when bar is changed', function (){
//$apply the change to trigger the $watch.
$scope.$apply();
//fetch().toHaveBeenCalled();
fetch();
it(" http ", function(){
//scope = $rootScope.$new();
var httpBackend;
httpBackend = $httpBackend;
httpBackend.when("GET", "https://api.stackexchange.com/2.2/search?page=1&pagesize=10&order=desc&sort=activity&intitle="+$scope.search+"&site=stackoverflow&filter=!4*Zo7ZC5C2H6BJxWq&key=DIoPmtUvEkXKjWdZB*d1nw((").respond([{}, {}, {}]);
});
});
});
First you should trigger the watch. For that you should change search value and after that manually run: $scope.$digest() or $scope.$apply()
In order to fully test the fetch function you should also mock the response to the second request (or the mock for all the second level requests if you want to test the iteration).
After that you should add some expect statements. For the controller code they should be:
expect($scope.items).toEqual(mockResponseToFirstRequest);
expect($scope.answers).toEqual(mockResponseCombinedForSecondLevelRequests);
As for using the spy in karma-jasmine tests, those limit the amount of the code tested. A plausible use for spy in this case is to replace the httpBackend.when with spyOn($http, 'get').and.callFake(function () {})
Here is the documentation for using spies https://jasmine.github.io/2.0/introduction.html#section-Spies
This is my service
angular.module('providers',)
.provider('sample', function(){
this.getName = function(){
return 'name';
};
this.$get = function($http, $log, $q, $localStorage, $sessionStorage) {
this.getTest = function(){
return 'test';
};
};
});
This is my unit test
describe('ProvideTest', function()
{
beforeEach(module("providers"));
beforeEach(function(){
module(function(sampleProvider){
sampleProviderObj=sampleProvider;
});
});
beforeEach(inject());
it('Should call Name', function()
{
expect(sampleProviderObj.getName()).toBe('name');
});
it('Should call test', function()
{
expect(sampleProviderObj.getTest()).toBe('test');
});
});
I am getting an error Type Error: 'undefined' is not a function evaluating sampleProviderObj.getTest()
I need a way to access function inside this.$get . Please help
You should inject your service into the test. Replace this:
beforeEach(function(){
module(function(sampleProvider){
sampleProviderObj=sampleProvider;
});
});
beforeEach(inject());
With this:
beforeEach(inject(function(_sampleProvider_) {
sampleProvider = _sampleProvider_;
}));
Firstly, you need, as had already been said, inject service, that you test. Like following
beforeEach(angular.mock.inject(function ($injector) {
sampleProviderObj = $injector.get('sample');
}));
Second, and more important thing. Sample have no any getTest functions. If you really need to test this function, you should as "Arrange" part of your test execute also $get function of your provider. And then test getTest function of result of previous execution. Like this:
it('Should call test', function()
{
var nestedObj = sampleProviderObj.$get(/*provide correct parameters for this function*/)
expect(nestedObj.getTest()).toBe('test');
});
But it's not good because this test can fail even if nestedObj.getTest work properly (in case when sampleProviderObj.$get works incorrect).
And one more thing, it seems like you need to inject this services $http, $log, $q, $localStorage, $sessionStorage to you provider rather then passing them as parameters.
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.
I'm a little new at Angular \ SinonJS so please forgive the silly question and bear with if this is obvious. I've done some googling and can't seem to find an answer. I've used SinonJs to do mocking as that was recommended in a Pluralsight video. Unsure if its the best choice. Any alternatives welcome.
I want to test the behaviour of my AngularJS controller and test that it calls my repository Search method with the criteria I specify only once.
I have the following in my controller and am getting the error in my Jasmin test runner:
goal-controller.js:
stepByStepApp.controller("goalController", function ($scope, goalRepository) {
$scope.viewGoalButtonDisabled = true;
$scope.search = function (criteria) {
$scope.errors = [];
return goalRepository.search(criteria).$promise.then(
function (goals) {
$scope.viewGoalButtonDisabled = true;
return goals;
},
function (response) {
$scope.viewGoalButtonDisabled = true;
$scope.errors = response.data;
});
};
});
goal-controller-tests.js
'use strict';
(function () {
describe('Given a Goal Controller', function () {
var scope, controller, goalRepositoryMock, goals, criteria;
beforeEach(function () {
module('stepByStepApp');
inject(function ($rootScope, $controller, goalRepository) {
scope = $rootScope.$new();
goalRepositoryMock = sinon.mock(goalRepository);
goals = [{ foo: 'bar' }];
criteria = 'test search criteria';
controller = $controller('goalController', { $scope: scope });
});
});
it('the View Goal Button should be disabled', function () {
expect(scope.viewGoalButtonDisabled).toBe(true);
});
describe("when a goal is searched for, it", function () {
it("should search the Goal Repository", function () {
goalRepositoryMock.expects('search').once().returns(goals);
scope.search(criteria);
goalRepositoryMock.verify();
});
});
});
}())
I am getting the following error:
2 specs, 1 failure
Given a Goal Controller
when a goal is searched for, it
should search the Goal Repository
TypeError: Cannot read property 'then' of undefined
I'm clearly not mocking the call to "goalRepository.search(criteria).$promise.then" properly. How do I mock the $promise and .then properly? Thanks in advance.
I'm assuming that this repository is returning a resource object. With that said, here is how I would go about testing this controller.
Here is the working plunk.
Initial beforeEach block
You need to mock out the promise chain. For that, you need to inject the $q service. Below is how my class wide beforeEach statement. I use stubs for mocking. I inject that mock into my controller under test.
beforeEach(inject(function($controller, $rootScope, $q) {
q = $q
scope = $rootScope;
goalRepositoryStub = sinon.stub({
search: function() {}
});
testCtrl = $controller("goalController", {
$scope: scope,
goalRepository: goalRepositoryStub
});
}));
With that mocked repository I now have complete control over what it does.
beforeEach for testing repo
In this block I actually mock out the entire promise chain. I get a defer object from the q service. From it I get a promise. I then put that promise in a fake resource object. And I then return that fake resource object whenever search is called. I then call the search on scope.
beforeEach(function() {
deferred = q.defer();
promise = deferred.promise;
returnedResource = {
$promise: promise
};
goalRepositoryStub.search.returns(returnedResource);
scope.search(criteria);
});
The Actual Testing
For the actual testing you need to tell that deferred object what to do (either reject or resolve the promise) and trigger the scopes $apply() function. You then test to see if your code is doing what it should be doing.
Here is an example of how I would test a successful call to goalRepository:
describe('successful goalRepository call', function() {
beforeEach(function() {
deferred.resolve(dataToReturn);
scope.$apply();
});
it('should add the data to scope.goals.', function() {
expect(scope.goals).toBe(dataToReturn);
});
it('should not change scope.failureApi to true.', function() {
expect(scope.viewGoalButtonDisabled).toBeFalsy();
});
});
These aren't necessary "best practices" or anything. Just a way that I found to solve this particular problem on my own.
I am trying to begin writing unit tests for my angular application and hit a stopping block pretty quick as I am unsure of how exactly to mock my service in a testable way.
Is there a way to mock the REST call otherwise it would seem like I need to mirror everything within my service in my tests which doesn't seem right to me, but I am rather new to test writing so maybe this is how it is supposed to be accomplished. Any help would be greatly appreciated.
My service is as follows:
angular.module('resources.users', ['ngResource'])
.factory('User', function($resource) {
var resource = $resource('/api/index.php/users/:username', {}, {
'update': {method: 'PUT'}
});
resource.getUser = function(username, successCb) {
return resource.query({username: username}, successCb);
};
return resource;
});
My test consists thus far of:
describe('User', function() {
var mockUserResource;
beforeEach(module('resources.users'));
beforeEach(function() {
mockUserResource = sinon.stub({
getUser: function(username) {
mockUserResource.query({username: username});
},
query: function() {}
});
module(function($provide) {
$provide.value('User', mockUserResource);
})
});
describe('getUser', function() {
it('should call getUser with username', inject(function(User) {
User.getUser('test');
expect(mockUserResource.query.args[0][0]).toEqual({username: 'test'});
}));
})
});
You can mock the requests made by ngResource like this:
describe('User', function () {
var mockUserResource, $httpBackend;
beforeEach(angular.mock.module('myApp'));
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
describe('getUser', function () {
it('should call getUser with username', inject(function (User) {
$httpBackend.expectGET('/api/index.php/users/test')
.respond([{
username: 'test'
}]);
var result = mockUserResource.getUser('test');
$httpBackend.flush();
expect(result[0].username).toEqual('test');
}));
});
});
Demo
zsong's answer greatly helped me understand this, but I would like to expand on how it works. In case it gets edited, I list the code again here:
describe('User', function () {
var mockUserResource, $httpBackend;
beforeEach(angular.mock.module('myApp'));
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
describe('getUser', function () {
it('should call getUser with username', inject(function (User) {
$httpBackend.expectGET('/api/index.php/users/test')
.respond([{
username: 'test'
}]);
var result = mockUserResource.getUser('test');
$httpBackend.flush();
expect(result[0].username).toEqual('test');
}));
});
});
What's going on here?
1
beforeEach(angular.mock.module('myApp'));
We tell the Angular injector ($injector and angular.mock.inject) to inject things defined in the myApp module. You can think of it as defining a module dependency without a dependent module. Compare with how things defined in the myApp module can be injected in, say, a controller in a angular.module('myOtherApp', ['myApp']) module.
2
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
Before each spec, run the function ($injector) function with dependencies injected. In this case, the dependency ($injector) is resolved implicitly from the parameter name. A functionally equivalent variant of this snippet is
beforeEach(function () {
angular.mock.inject(['$httpBackend', 'User', function ($httpB, User) {
$httpBackend = $httpB;
mockUserResource = User;
}]);
});
Here we have instead declared the dependencies explicitly, and are free to use any parameter names we wish.
3
it('should call getUser with username', inject(function (User) {
Again, the test function is injected with the implicitly resolved User service as a parameter, though it isn't actually used.
Notice that this time there is no wrapper function around the inject call. inject invokes the passed function immediately if a spec is currently running, but otherwise it returns a wrapper function (see the inject docs and source code), so we don't actually need the wrapper function. Thus, we could have written the beforeEach snippet above like this:
beforeEach(angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
}));