Ive seen such questions and they all say 'extract logic out into a service and mock the service'. Simple, except i have as much as possible.
So when the controller inits i have a call to $scope.getWidgets()
this method calls widgetService
to get a list of widgets for the user, it will then display a toast notification using notifyService
. Should widgetService
reject its promise or the user have no widgets a call to notifyService
is made. This is all done when the controller inits.
$scope.getWidgets = function () {
widgetService.getWidgets()
.then(function (widgets) {
if (widgets.length === 0) {
notifyService.notify('no widgets');
}
$scope.widgets = widgets;
})
.catch(function (e) {
notifyService.notify('oh noes');
});
}
//called at bottom of controller
$scope.getWidgets();
Now all my tests so for have not had need to run any digest cycles, run running promises etc. The method im trying to test calls a service to save a widget. Should it succeed or fail it will again call notifyService
to send a notification to the user. I'm testing that the notification is triggered using karma's spyOn
and also testing some flags are set correctly. There's no real logic, thats done in the service.
$scope.saveWidget = function (widget) {
widgetService.saveWidget(widget)
.then(function () {
notifyService.notify('all dandy');
//set some scope flags, no logic
})
.catch(function (e) {
notifyService.notify('oh noes');
//set some more scope flags, no logic
});
}
So now i have have to run the digest cycle to trigger my promises. The Trouble is my mock for widgetService.getWidgets()
returns a promise. So when i run the digest first it resolves the promise registered in my init, which calls the notify service, which activates my spyon, concluding my test with false data.
Here are my tests, (please forgive the big code block, ive tried to strip it down as much as possible).
describe('tests', function () {
var scope, controlelr, _mockWidgetService, _mockNotifyService;
beforeEach(function () {
//Init the service mocks, angular.noop for methods etc
_mockWidgetService = init();
_mockNotifyService = init();
});
beforeEach(function () {
//Regisetr hooks to pull in my mocked services
angular.mock.module('myModule');
angular.mock.module(function($provide) {
$provide.value('widgetService', _mockWidgetService);
$provide.value('notifyService', _mockNotifyService);
})
});
angular.mock.inject(function ($controller, $rootScope, widgetService, notifyService) {
//create scope and inject services to create controller
scope = $rootScope.$new();
//Trouble is $scope.getWidgets() is called now.
controller = $controller('testController', {
$scope: scope,
widgetService: widgetService,
notifyService: notifyService
});
});
describe('getWidgets', function() {
it('myTest', function () {
//I cannow manipulate mock services moreso if needed just for this test
spyOn(_mockNotifyService, 'notfy');
//Call save widget
scope.saveWidget();
scope.$digest();
expect(_mockNotifyService.notify).toHaveBeenCalledWith(//values);
});
});
});
So as you can see im struggling, i could mock all the mocks required for the init code, but id rather only mock the services i need for that test. Plus i don't like the idea of the init code running every single test potentially causing false errors (it has its own tests).
Ive seen $onInit
event the controller calls, i had the idea of putting the init call in that but i don't see how i can intercept that to prevent it running for the tests that dont need it. Any advise or solutions to such use cases would greatly appreciated.
Aucun commentaire:
Enregistrer un commentaire