为$route.current设置测试值.AngularJS单元测试中的参数



我正在使用O'Reilly的AngularJS书籍来学习AngularJS。然后在书中创建一个服务,它的任务是加载$resource,如下所示:

services.factory('Recipe', ['$resource', function($resource) {
  return $resource('/recipes/:id', {id: '@id'});
}]);
services.factory('RecipeLoader', ['Recipe', '$route', '$q', function(Recipe, $route, $q) {
  return function() {
    var delay = $q.defer();
    Recipe.get({id: $route.current.params.recipeId}, function(recipe) {
      delay.resolve(recipe);
    }, function() {
      delay.reject('Unable to fetch recipe '  + $route.current.params.recipeId);
    });
    return delay.promise;
  };
});

然而,在书中忽略了测试这个服务,我正在努力找出如何做到这一点。我已经创建了一个使用模拟$httpBackend的测试,但我遇到的问题是当测试运行$route时。当前未定义

我已经尝试注入$route到我的测试和设置$route.current.params。Id = 1;但这似乎行不通。有人对如何在$route.current.params中设置测试值有任何建议吗?

多谢! !

解决方案

我找到了一个更好的解决方案,我让工厂把id作为参数,像这样:

services.factory('RecipeLoader', ['Recipe', '$route', '$q', function(Recipe, $route, $q) {
  return function(recipeId) {
    var delay = $q.defer();
    Recipe.get({id: recipeId}, function(recipe) {
      delay.resolve(recipe);
    }, function() {
      delay.reject('Unable to fetch recipe '  + $route.current.params.recipeId);
    });
    return delay.promise;
  };
});

然后修改$routeProvider中的resolve参数,传入ID。就像

.when('/recipe/view/:id', {
  templateUrl: 'views/recipe/view.html',
  controller: 'RecipeViewCtrl',
  resolve: {
    renewal: function (RecipeLoader, $route) {
      return RecipeLoader($route.current.params.id);
    }
  }
})

我觉得这是更好的设计,因为控制器更具体地说明了它需要什么输入。

您是正确的,您偶然发现的解决方案是创建此服务并对其进行测试的更好方法。对于服务,它应该独立于魔法,就像路由或状态一样,并且应该传入它需要的东西。

书中的代码更多地展示了如何同时使用服务和解析,而不是编写服务的正确方法。

对于服务,只要可能,更倾向于传递参数,而不是从魔法状态读取参数。

也就是说,这是一个有趣的问题,当你需要测试依赖于某些服务的其他服务时,你如何模拟它们。

有两种方法可以在测试中覆盖服务:

  1. 为单个单元测试或描述块的目的重写它。在使用beforeEach(module('myModule'))初始化您自己的模块之后,您可以创建一个内联模块,如下所示:

    beforeEach(module(function($provide) {
       $provide.factory('ServiceToOverride', function() {
           // Return overriden service here
       });
    }));
    

    链接:Plnkr

  2. 创建一个用于通用mock的模块,例如angular.module('myApp.mock'),并在其中定义您的覆盖服务。然后,在beforeEach加载测试中的模块之后,还使用beforeEach(module('myApp.mock'));

  3. 加载此模块。

后者对于更密集和逻辑封装的模拟更有用,而前者对于快速、一次性、单次使用的模拟非常有用。

最新更新