In my Controller I've defined the following service:
CrudService.getAllGroups().$promise.then(
function (response) { $scope.groups = response; },
function (error) { //error code.. }
);
Well, I want to test this service whether it gets a response or not. In test script at first I've defined a function to check whether the service is defined at all.
Test code:
describe('Ctrl: TestCtrl', function () {
beforeEach(module('testApp'));
var scope,
CrudService,
ctrl,
backend;
beforeEach(inject(function ($controller, $rootScope, _CrudService_, $httpBackend) {
scope = $rootScope.$new();
ctrl = $controller('TestCtrl', {
$scope: scope
});
CrudService = _CrudService_;
backend = $httpBackend;
}));
it('should defined the service getGroups', function () {
expect(CrudService.getGroups).toBeDefined();
});
//this is wrong!
it('should returns a successful response', function () {
backend.expectGET('http://localhost:63831/api/group').respond(200, 'success');
backend.flush();
});
});
I don't know how to get a response in the test. I'm new in unit testing and need some help.
For a better comprehension here is the service code:
//CrudService file:
...
return {
getAllGroups: function () {
return ResService.group.query();
}
}
...
//ResService file:
return {
group: $resource(baseUrl + '/api/group/:Id', {
Id: '#Id'
}, {})
}
Do anyone has an idea?
It's incorrect in the sense that it's not a unit test. If you are testing controller here, then you should mock CrudService and test that $scope.groups has been assigned correctly.
beforeEach(function () {
module(function ($provide) {
$provide.factory('CrudService', function () {
return {
getAllGroups: function () {
return {
$promise: null // return an actual promise here
}
}
}
});
});
});
it('should set groups', function () {
expect($scope.groups).toEqual('success')
});
And you need a separate spec to test if CrudService calling backend correctly.
Related
I am new to angularjs unit testing. I have a factory I am trying to spy on with jasmine and I can't figure out the syntax for the test spec. Below is the factory:
app.factory('assetFactory', function ($http) {
var baseAddress = "../api/";
var url = "";
var factory = {};
factory.getAssets = function (term) {
url = baseAddress + "asset/search/" + term;
return $http.get(url);
};
return factory;
});
Here is my test spec, which fails on the expect statement (Error: Expected spy getAssets to have been called):
describe('assetFactory', function () {
beforeEach(function () {
module('fixedAssetApp');
});
beforeEach(inject(function (assetFactory) {
spyOn(assetFactory, 'getAssets').and.callThrough();
}));
it('should be defined', inject(function (assetFactory) {
expect(assetFactory).toBeDefined();
}));
it('should have been called, inject(function (assetFactory) {
expect(assetFactory.getAssets).toHaveBeenCalled();
}));
});
Please add this change.
beforeEach(inject(function (assetFactory) {
spyOn(assetFactory, 'getAssets').and.callThrough();
assetFactory.getAssets();
}));
In order to toHaveBeenCalled() return true, you must called your function either in beforeEach or it block.
My Angular 1.3 application is using the angular-translate library. In my Karma tests I'm attempting to mock the $translate provider with a Mock object I have created.
The mock object is called MockTranslate and it belongs to the myMocks module. I'm not including the source for MockTranslate in the question as it's not relevant to the question.
The subject of my test is a controller and I can quite easily mock $translate using the following:
module('myMocks');
inject(function($controller, MockTranslate) {
$controller("MyController", {
$translate: MockTranslate.create(translations);
});
});
The above mocking works, however my preference would be to mock the provider using the angular.mock.module with something like:
module('myMocks');
module("myModule", function($provide) {
$provide.provider("$translate", function(MockTranslate) {
return MockTranslate.create(translations);
});
});
But I get the following error when I run my tests:
Error: [$injector:modulerr] Failed to instantiate module function ($provide) due to: Error: [$injector:unpr] Unknown provider: MockTranslate
How do I mock a provider using angular.mock.module?
If I understood the task correctly then here is a working example:
angular.module('translateApp', [])
.controller('translateCtrl', function ($scope, $translate) {
$scope.translate = function(message) {
return $translate.translate(message);
};
})
.provider({
$translate: function() {
this.$get = function () {
return {
translate: function (msg) {
return 'OriginalTranslate: ' + msg;
}
};
};
}
});
describe('Translate Controller Test', function() {
var mockScope;
var mockTranslate;
beforeEach(module('translateApp', function($provide) {
$provide.provider('MockTranslate', function() {
this.$get = function () {
return {
translate: function (msg) {
return 'MockTranslate: ' + msg;
}
};
}
});
$provide.provider('$translate', function() {
this.$get = function (MockTranslate) {
return {
translate: function (msg) {
return MockTranslate.translate(msg);
}
};
}
});
}));
beforeEach(inject(function($controller, $rootScope, $translate) {
mockScope = $rootScope.$new();
mockTranslate = $translate;
$controller('translateCtrl', {
$scope: mockScope,
$translate: mockTranslate
});
}));
it('Translates messages', function () {
expect(mockScope.translate('cool message')).toEqual('MockTranslate: cool message');
});
});
We're unit testing our services and facing issue spying on methods with arguments of dependent services.
I am writing unit tests for ServiceA
ServiceA.js
angular.module("App").service("ServiceA", function($http, ServiceB) {
this.detail = null;
this.method = function(id){
var sevrB = new ServiceB();
return sevrB.getId(1).then(function(response) {
this.detail = response.data;
});
};
});
ServiceB.js (is a factory)
(function () {
var dependencies = [
'../module'
];
define(dependencies, function (module) {
return module.factory('ServiceB', function ($http) {
var ServiceB= function () {
this.id = null;
};
ServiceB.prototype.getId = function(Id) {
return $http.get('/test/');
}
}
}());
Unit test code
describe('Testing ServiceA', function () {
var serviceA, serviceBMock;
beforeEach(function () {
var _serviceBMock = function () {
return {
getId:function(id){
return 'test';
}
};
};
angular.module('ServiceAMocks', [])
.value('ServiceB', _serviceBMock);
});
beforeEach(module('ServiceAMocks'));
beforeEach(inject(function (_ServiceA_, _ServiceB_) {
serviceA=_ServiceA_;
serviceBMock=_ServiceB_;
});
it('retrive Id', function () {
spyOn(serviceBMock,'getId').and.Return('test');
serviceA.method(1);
});
});
I am spying on getId method of ServiceB from ServiceA and if i mocked ServiceB as function i am getting error below
Error: getId() method does not exist
at jasmineInterface.spyOn
If I mock serviceB as object then i get error as
TypeError: object is not a function
var _serviceBMock = {
getId:function(id){
return 'test';
}
}
And I am not sure of testing promise in this scenario.
This version supports Jasmine 1.3
I’m injecting $q service as ServiceB wants to call method then. We can even go forward and resolve returned promise, but this is next step in testing.
Answer to previous version of question, where AngularJS injects instance of serviceB
describe('ServiceA', function () {
var serviceA, ServiceB, $q;
beforeEach(function () {
module('App');
});
beforeEach(function () {
module(function ($provide) {
$provide.value('ServiceB', {
getId: jasmine.createSpy('ServiceB.getId').andCallFake(function () {
return $q.all();
})
});
});
});
beforeEach(inject(function (_ServiceA_, _ServiceB_, _$q_) {
$q = _$q_;
serviceA = _ServiceA_;
ServiceB = _ServiceB_;
}));
describe('.method()', function () {
it('returns ServiceB.getId() argument', function () {
serviceA.method(1);
expect(ServiceB.getId).toHaveBeenCalledWith(1);
});
});
});
jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/sDh35/
I have created an Angular factory that has methods which handle saving code to a server. One of the factory methods contains a third party object which has a method which does the actual callout. I would like to test this code, but I can't work out how to mock out the third party object.
I have set up a plunker with a Jasmine test.
My aim for this test is just to successfully get the code to use my mock object rather than the ThirdPartySavingUtils object. Is that possible?
var app = angular.module("MyApp", []);
app.factory("SavingUtils", function() {
return {
saveStuff: function() {
if(typeof ThirdPartySavingUtils !== "undefined") {
return ThirdPartySavingUtils.value;
}
}
};
});
this is my jasmine tests
describe("Mocking Test", function() {
var ThirdPartySavingUtilsMock;
var SavingUtils;
beforeEach(function() {
angular.mock.module("MyApp", function($provide) {
ThirdPartySavingUtilsMock = {
value: "I am the mock object"
};
$provide.value("ThirdPartySavingUtils", ThirdPartySavingUtilsMock);
});
inject(function(_SavingUtils_) {
SavingUtils = _SavingUtils_;
});
});
it("should run without throwing an exception", function() {
expect(true).toBe(true);
});
it("should mock out ThirdPartySavingUtils with ThirdPartySavingUtilsMock", function() {
var result = SavingUtils.saveStuff();
expect(result).toEqual("I am the mock object");
});
});
You have a few options really but more than likely you would need to do both.
1) You could create an angular service which wraps this third party object - this way you get a nice abstraction incase you ever need to change the third party object.
2) You could use a mocking framework like http://sinonjs.org/ which enable you to mock methods out and do asserts like calledOnce etc.
Here is a link to a mocked test using sinon test.
You can bascially see sinon is used as a sandbox to mock out an object methods. Sinon provides extra propeties to those mocked methods so you can assert if they were called, the parameters they were called with even the order of the calls. It is a really, really essential testing tool.
describe('validationManager', function () {
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$q = $injector.get('$q');
defer = $q.defer();
validator = $injector.get('validator');
validationManager = $injector.get('validationManager');
sandbox.stub(validator, 'makeValid');
sandbox.stub(validator, 'makeInvalid');
sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);
setModelCtrl();
}));
afterEach(function () {
sandbox.restore();
setModelCtrl();
});
it('should be defined', function () {
expect(validationManager).to.exist;
});
describe('validateElement', function () {
it('should return if no $parsers or $formatters on the controller', function () {
validationManager.validateElement(modelCtrl);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
});
EDIT -----------------------
Here this put into practice for your code (I haven't run this but it give the general idea).
(function (angular, ThirdPartyApi) {
'use strict';
var app = angular.module('MyApp', []);
app.factory('thirdPartApi', [
function () {
return {
save: ThirdPartyApi.save,
value: ThirdPartyApi.value
};
}
]);
app.factory('SavingUtils', [
'thirdPartApi',
function (thirdPartApi) {
var getValue = function () {
return thirdPartApi.value;
},
save = function (item) {
return thirdPartApi.save(item);
};
return {
save: save,
getValue: getValue
};
}
]);
}(angular, window.ThirdPartyApi));
The tests.....
(function (angular, sinon) {
'use strict';
describe('MyApp.SavingUtils', function () {
var sandbox, thirdPartyApi, SavingUtils, thirdPartyApiValue = 2;
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
thirdPartyApi = $injector.get('thirdPartyApi');
SavingUtils = $injector.get('SavingUtils');
// stub the method and when called return a simple object or whatever you want
sandbox.stub(thirdPartyApi, 'save').returns({ id: 1});
sandbox.stub(thirdPartyApi, 'value', function () {
return thirdPartyApiValue;
});
}));
afterEach(function () {
// This removes those stubs and replace the original methods/values
sandbox.restore();
});
describe('save', function () {
it('should return call the save method on thirdPartyApi', function () {
var item = {};
SavingUtils.save(item);
expect(thirdPartyApi.save.calledOnce).to.equal(true);
});
});
describe('getValue', function () {
it('should return value of value property on thirdPartyApi', function () {
var result = SavingUtils.getValue();
expect(result).to.equal(thirdPartyApiValue);
});
});
});
}(angular, sinon));
I have a ParseService, that I would like to mock in order test all the controllers that are using it, I have been reading about jasmine spies but it is still unclear for me. Could anybody give me an example of how to mock a custom service and use it in the Controller test?
Right now I have a Controller that uses a Service to insert a book:
BookCrossingApp.controller('AddBookCtrl', function ($scope, DataService, $location) {
$scope.registerNewBook = function (book) {
DataService.registerBook(book, function (isResult, result) {
$scope.$apply(function () {
$scope.registerResult = isResult ? "Success" : result;
});
if (isResult) {
//$scope.registerResult = "Success";
$location.path('/main');
}
else {
$scope.registerResult = "Fail!";
//$location.path('/');
}
});
};
});
The service is like this:
angular.module('DataServices', [])
/**
* Parse Service
* Use Parse.com as a back-end for the application.
*/
.factory('ParseService', function () {
var ParseService = {
name: "Parse",
registerBook: function registerBook(bookk, callback) {
var book = new Book();
book.set("title", bookk.title);
book.set("description", bookk.Description);
book.set("registrationId", bookk.RegistrationId);
var newAcl = new Parse.ACL(Parse.User.current());
newAcl.setPublicReadAccess(true);
book.setACL(newAcl);
book.save(null, {
success: function (book) {
// The object was saved successfully.
callback(true, null);
},
error: function (book, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
callback(false, error);
}
});
}
};
return ParseService;
});
And my test so far look like this:
describe('Controller: AddBookCtrl', function() {
// // load the controller's module
beforeEach(module('BookCrossingApp'));
var AddBookCtrl, scope, book;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope;
book = {title: "fooTitle13"};
AddBookCtrl = $controller('AddBookCtrl', {
$scope: scope
});
}));
it('should call Parse Service method', function () {
//We need to get the injector from angular
var $injector = angular.injector([ 'DataServices' ]);
//We get the service from the injector that we have called
var mockService = $injector.get( 'ParseService' );
mockService.registerBook = jasmine.createSpy("registerBook");
scope.registerNewBook(book);
//With this call we SPY the method registerBook of our mockservice
//we have to make sure that the register book have been called after the call of our Controller
expect(mockService.registerBook).toHaveBeenCalled();
});
it('Dummy test', function () {
expect(true).toBe(true);
});
});
Right now the test is failing:
Expected spy registerBook to have been called.
Error: Expected spy registerBook to have been called.
What I am doing wrong?
What I was doing wrong is not injecting the Mocked Service into the controller in the beforeEach:
describe('Controller: AddBookCtrl', function() {
var scope;
var ParseServiceMock;
var AddBookCtrl;
// load the controller's module
beforeEach(module('BookCrossingApp'));
// define the mock Parse service
beforeEach(function() {
ParseServiceMock = {
registerBook: function(book) {},
getBookRegistrationId: function() {}
};
});
// inject the required services and instantiate the controller
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
AddBookCtrl = $controller('AddBookCtrl', {
$scope: scope,
DataService: ParseServiceMock
});
}));
it('should call registerBook Parse Service method', function () {
var book = {title: "fooTitle"}
spyOn(ParseServiceMock, 'registerBook').andCallThrough();
//spyOn(ParseServiceMock, 'getBookRegistrationId').andCallThrough();
scope.registerNewBook(book);
expect(ParseServiceMock.registerBook).toHaveBeenCalled();
//expect(ParseServiceMock.getBookRegistrationId).toHaveBeenCalled();
});
});
You can inject your service and then use spyOn.and.returnValue() like this:
beforeEach(angular.mock.module('yourModule'));
beforeEach(angular.mock.inject(function($rootScope, $controller, ParseService) {
mock = {
$scope: $rootScope.$new(),
ParseService: ParseService
};
$controller('AddBookCtrl', mock);
}));
it('should call Parse Service method', function () {
spyOn(mock.ParseService, "registerBook").and.returnValue({id: 3});
mock.$scope.registerNewBook();
expect(mock.ParseService.registerBook).toHaveBeenCalled();
});
Following Javito's answer 4 years after-the-fact. Jasmine changed their syntax in 2.0 for calling through to real methods on spies.
Change:
spyOn(ParseServiceMock, 'registerBook').andCallThrough();
to:
spyOn(ParseServiceMock, 'registerBook').and.callThrough();
Source
Include angular-mocks.js in your project and read carefully through the following link.