I am mocking out a service for an AngularJS Unit Test. I'm using the $provide
service to replace the 'real' service with the mocked out one (a plunker script of this is available):
describe('My Controller', function () {
var $scope;
var $provide;
beforeEach(angular.mock.module('myApp'));
beforeEach(angular.mock.module(function (_$provide_) {
$provide = _$provide_;
}));
beforeEach(angular.mock.inject(function($rootScope, $controller, $q){
var mockMyService = {
getAll : function() {
var deferred = $q.defer();
deferred.resolve([
{ itemText: "Foo" },
{ itemText: "Bar" }
]);
return deferred.promise;
}
};
$provide.value('myService', mockMyService);
$scope = $rootScope.$new();
$controller('MyCtrl', { $scope: $scope });
$rootScope.$apply();
}));
it('Has two items defined', function () {
expect($scope.items.length).toEqual(2);
});
});
This works just fine. However, I don't like the fact that I am using an angular.mock.module
function simply to give a reference to the $provide
service which is then used in the angular.mock.inject
function below. But if I add $provide
as a parameter to the angular.mock.inject
function directly instead, I get an 'unknown provider' error.
It occurs to me that I could put all the mocking code in the angular.mock.module
function. But then I have a similar issue with the $q
reference, which I need as my mocked service has to return a promise.
In other words, if I add a $q
parameter to the angular.mock.module
function then I also get an 'unknown provider' error.
Is there a way to simplify this? Obviously what I have works but it doesn't feel quite right, somehow. I feel that I lack understanding of why some providers are available in inject
functions and others are available in module
functions.
A mock module provides: mocks of all components, directives, pipes and providers. mocks of all imports and exports. dummy clones of all services.
To test a service, you set the providers metadata property with an array of the services that you'll test or mock. content_copy let service: ValueService; beforeEach(() => { TestBed. configureTestingModule({ providers: [ValueService] }); }); Then inject it inside a test by calling TestBed.
You can't use $provide
within the inject
function because the former registers providers for the latter to use. Take a look:
describe('...', function() {
beforeEach(function() {
module(function($provide) {
$provide.constant('someValue', 'foobar');
});
inject(function(someValue) {
var value = someValue; // will be 'foobar';
});
});
});
You can though write your test this way:
describe('...', function() {
var serviceMock;
beforeEach(function() {
serviceMock = {
someMethod: function() { ... }
};
module(function($provide) {
$provide.value('service', serviceMock);
});
inject(function(service) {
...
});
});
});
In fact, you don't even need to implement the mocked service before injecting it with $provide
:
beforeEach(function() {
serviceMock = {};
module(function($provide) {
$provide.value('service', serviceMock);
});
inject(function(service) {
...
});
});
it('tests something', function() {
// Arrange
serviceMock.someMethod = function() { ... }
// Act
// does something
// Assert
expect(...).toBe(...);
});
Here's a Plunker script illustrating mostly of the above.
This worked for me when I had to wrap a service which used $q
and seems quite clean:
var _ServiceToTest_;
beforeEach(function () {
module('module.being.tested');
module(function ($provide) {
$provide.factory('ServiceToMock', function ($q, $rootScope) {
var service = ...;
// use $q et al to heart's content
return service;
});
});
inject(function (_ServiceToTest_) {
ServiceToTest = _ServiceToTest_;
});
});
it('...', function () { /* code using ServiceToTest */ });
The trick was to use $provide.factory
instead of $provide.value
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With