I have a service which is being tested:
angular.module('services', []).
service('myService', function(topService) {});
And the topService is another angularjs service.
Now I want to write unit test for myService, but I need to mock the topService and pass it to myService.
I tried but not sure how to make it work:
define(['angular', 'angularMocks', 'services/my'], function(_, mocks, _) {
beforeEach(mocks.module('services'));
describe("my service", function() {
it("should do something", mocks.inject(function(myService) {
// how to mock and inject a `topService` to `myService` ????
expect(myService).doSomething().toEqual("???");
}));
});
});
How to do that?
First create the mockedTopService, provide any function which will be needed by the test to run:
var mockedtopService = {
doSomething: function() {
return "test";
}
};
Then provide it to angular using $provide:
beforeEach(function () {
module(function ($provide) {
$provide.value('topService', mockedtopService );
});
}
In case you need to get an instance of the service via angular you can do it this way:
inject(function (topService) {
var topServiceInstance = topService;
});
Related
I am trying to unit test my controller. The function that I am trying to unit test is:
function myFunction() {
MyService
.myMethod(thing1, thing2)
.then(function handleMyMethod(result) {
SomeModule.errorHandler(result)
.onSuccess(function onSuccess() {
// do stuff
})
.onError(function onError() {
// do stuff
});
});
}
Relevant test file snippet:
var MockService = {
myMethod: function(thing1, thing2) {
var promise = $q.defer().promise;
return promise;
}
};
beforeEach(module(function($provide) {
$provide.value('MyService', MockService);
}));
beforeEach(inject(function (_$controller_, _MyService_, _SomeModule_, ...) {
...
MyService = _MyService_;
MyController = _$controller_('MyController as Ctrl', {
$controller: controller,
MyService: MockService,
});
I am confused about how to write tests that allow me to hit both the onSuccess and onError cases. I am trying to cover both branches for branch coverage, but don't know how the syntax works.
You can do it one of two ways:
You can write your mock service to look at the parameters and resolve with an error or success.
myMethod:function(thing1, thing2) {
if (thing1=='all good') return $q.when('excellent');
return $q.reject('sorry, bud');
}
You can override the method closer to where you're calling it.
it('is a success', function() {
spyOn(MockService, 'myMethod').and.returnValue($q.when('excellent');
$rootScope.$apply(
MyController.doStuff('foo');
);
expect(MyController.someProperty).toEqual('excellent');
});
//etc.
Note you don't need to both override the module injector with the provide code and provide the mock service in the $controller locals parameter.
I have a controller that immediately grabs some data from a service when it loads.
angular.module('app').controller("MyController", function (myService) {
var projectId = myservice.project.id;
});
This data gets set from a previous action in the application. So when karma/jasmine loads this controller in a fresh state this service doesn't have this data. I've tried mocking this service in a beforeEach block but haven't had any luck.
beforeEach(function () {
var myService = {
project: {
id: 'thadsfkasj'
}
}
});
What am I missing?
You can mock your service using angular.value. This should allow you to then inject myService into your controller.
angular.module('myServiceMock', [])
.value('myService',
{
project: {
id: 'thadsfkasj'
}
});
beforeEach(module(
'myServiceMock'
//other modules or mocks to include...
));
Are you using ngMock as well?
If so, you should do the following:
beforeEach(module(function ($provide) {
var myService = {
project: {
id: 'thadsfkasj'
}
}
$provide.value('myService', myService);
}));
Test AngularJS factory function with Jasmine
Without having to deal with injecting $provide or overriding modules, you can simply pass in an object representing your mock when you instantiate your controller in your tests.
beforeEach(inject(function (_$controller_) {
myMockService = {
project: {
id: 100
}
};
ctrl = _$controller_('MyController', {
...
myService: myMockService
...
});
}));
I am trying to test an angularjs service called MyService. If I try to inject it, seems that angular tries to use it before is loaded. On the other hand, if I mock MyService via $provide and so on, it works but I will not have the actual object to test.
(function (angular) {
'use strict';
angular.module('app', []).run(["MyService",
function (MyService) {
MyService.initListeners();
}
]);
// this is supposed to be in another file
angular.module('app')
.service("MyService", function() {
return {
initListeners: function() {
console.log("working")
}
}
})
}(angular));
The test is this:
(function () {
'use strict';
describe("MyService", function () {
var MyService = null;
beforeEach(module("app"));
beforeEach(inject(function ($injector) {
MyService = $injector.get('MyService');
}));
afterEach(function () {
MyService = null;
});
it("injection works", function () {
expect(true).toBeTruthy(); // throws exception
});
});
}());
I did the test on a jsfiddle.
I see the order of execution with some console messages. The order of execution is the correct, as expected.
My service constructor
before init
MyService.initListeners()
after init
And the two test work correctly:
it("injection works", function () {
expect(true).toBeTruthy(); // throws exception
expect(MyService).toBeDefined();
});
Here is the code: http://jsfiddle.net/jordiburgos/1efvof3k/
It could be your AngularJS version, Jasmine, etc...
So I have two services:
// The service I'm testing
angular.module("m").service("myService", function(otherService) { ... })
// the service I'd like to mock while testing
angular.module("m").service("otherService", function() { ... })
describe("my test", function() {
var myService = null;
beforeEach(module('m'));
beforeEach(inject(function($injector) {
///////////////////////////////////////////////////////
// but I want it to get injected with 'otherService'
///////////////////////////////////////////////////////
myService = $injector.get("myService")
})
it ('test myService', function() {
})
})
I want to mock out otherService before it's injected into myService and I test the instance of myService in follow up it functions.
You should use the $provide service to replace the otherService implementation with a mocked one. Here you go:
describe('my test', function() {
var myService, otherServiceMock;
beforeEach(function() {
module('m');
otherServiceMock = jasmine.createSpyObj('otherService', [...]);
module(function($provide) {
// Replaces the service with a mock object
$provide.value('otherService', otherServiceMock);
});
inject(function(_myService_) {
myService = _myService_;
});
});
});
Check out the $provide documentation for more information.
You can just mock the methods of the service in question on the fly
var myService, otherService;
beforeEach(inject(function($injector) {
myService = $injector.get('myService');
otherService = $injector.get('otherService');
}));
it('calls otherService.doOther when doSomething is called', function() {
spyOn(otherService, 'doOther');
myService.doSomething();
expect(otherService.doOther).toHaveBeenCalled();
});
With the jasmine spies, you can for example test outcomes with different return values, etc.
it('doesSomething returns true when otherService.doOther returns false', function() {
spyOn(otherService, 'doOther').andReturn(false);
expect(myService.doSomething()).toBeTruthy();
});
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.