问题
What is the best approach to mock $stateParams in a directive? $stateParam members will be changed according to the test.
I can easily mock $stateParams in a controller using $controller('ctrl', $stateParams) but dont know how to modify $stateParams that gets injected into the directive.
I've gone the route of decorating $stateParams with the below but can only declare that when i create the module. as i mentioned, $stateParam members will change many times through the different tests.
beforeEach(angular.mock.module(function ($provide) {
$provide.provider('$stateParams', function () {
return {
myStateParam: true,
myOtherStateParam: 'some text'
};
});
}));
回答1:
Using directive controller to handle route-related logic ($state
/$route
) is always a good idea, that's the job for controller and not for linking functions.
In the case of a directive that could be tested completely just by testing its controller and mocking its local dependencies (credits go to JB Nizet's answer) it is the end of the story:
$controller('...', { $scope: scope, $stateParams: { ... });
It isn't the case for regular directive spec with $compile
. $compile
uses $controller
internally to instantiate the controller but doesn't accept locals to mock its dependencies.
There are several ways to handle this.
$provide.factory
If $stateParams
does not have to be injected into spec directly and thus is uninstantiated, its value can be defined after the module was bootstrapped. When service instance value should be mocked only once per spec, look no further:
var stateParams;
beforeEach(module(('...', function ($provide) {
$provide.factory('$stateParams', function () {
return stateParams;
});
}));
it('...', inject(function ($compile) {
stateParams = { ... };
...
$provide.constant
$provide
service provider can be exposed as service instance to (re)define services after the module was bootstrapped:
beforeEach(module('...', function ($provide) {
$provide.value('$provide', $provide);
}));
it('...', inject(function ($provide, $compile) {
$provide.constant('$stateParams', { ... });
...
$provide.constant
is preferable in this case because it will replace cached $stateParams
service instance if it was injected before, while $provide.value
won't.
angular.copy
angular.copy
(which actually was used by UI Router 0.x to keep $stateParams
object up to date) can be used to preserve existing object references but replace its contents:
it('...', inject(function ($stateParams, $compile) {
angular.copy({ ... }, $stateParams);
...
$state.params
$state
service has little-known yet publicly exposed to API property params
, it can be used as a replacement for $stateParams
service all over the code to improve its testablity.
$stateParams
and $state.params
may not refer to the same object, so the choice has to be made in favour of one of them.
At the cost of the risk of test fragility due to of unsolicited $state
injection, it can be as neat as
it('...', inject(function ($state, $compile) {
$state.params = { ... };
...
来源:https://stackoverflow.com/questions/35899581/angular-jasmine-mock-stateparams-in-a-directive