Angularjs 如何在Angular js中使用firebase factory为控制器编写测试?

Angularjs 如何在Angular js中使用firebase factory为控制器编写测试?,angularjs,firebase,karma-jasmine,Angularjs,Firebase,Karma Jasmine,由于我分别编写了firebase factory和RecipeController,因此测试中出现了一个错误 TypeError: Cannot read property '$loaded' of undefined. $loaded是firebase中的一种方法 test.js describe('RecipeController', function() { beforeEach(module('leChef')); var $controller; beforeEach(injec

由于我分别编写了
firebase factory
RecipeController
,因此测试中出现了一个错误

TypeError: Cannot read property '$loaded' of undefined.
$loaded
firebase
中的一种方法

test.js

describe('RecipeController', function() {
beforeEach(module('leChef'));

var $controller;

beforeEach(inject(function(_$controller_){
  $controller = _$controller_;
}));

describe("$scope.calculateAverage", function() {
  it("calculates average correctly", function() {
    var $scope = {};
    var controller = $controller('RecipeController', { $scope: $scope });

    $scope.calculateAverage();

    expect(average).toBe(sum/(Recipes.reviews.length-1));
  });
});
});
firebase-factory.js

app.factory("Recipes", ["$firebaseArray",
   function($firebaseArray) {
   var ref = new Firebase("https://fiery-inferno-8595.firebaseio.com/recipes/");
   return $firebaseArray(ref);
  }
]);
recipe-controller.js

 app.controller("RecipeController", ["$scope", "toastr", "$location", "$routeParams", "$compile", "Recipes",
 function($scope, toastr, $location, $routeParams, $compile, Recipes) {

$scope.recipes.$loaded().then(function(payload) {
  $scope.recipe = payload.$getRecord($routeParams.id);

  $scope.html = $scope.recipe.instructions;

  if (typeof $scope.recipe.reviews === "undefined") {
    $scope.recipe.reviews = [{}];
  }

  $scope.calculateAverage = function(AverageData){
    var sum = 0;

    if ($scope.recipe.reviews.length > 1) {
      for(var i = 1; i < $scope.recipe.reviews.length; i++){
        sum += parseInt($scope.recipe.reviews[i].stars, 10);
      }

      var average = sum/($scope.recipe.reviews.length-1);
      var roundedAverage = Math.round(average);
      return { 
        average: roundedAverage,
        markedStars: new Array(roundedAverage)
      };
    } else {
      return sum;
    }
  };
});
]);
app.controller(“RecipeController”、[“$scope”、“toastr”、“$location”、“$routeParams”、“$compile”、“Recipes”,
函数($scope,toastr,$location,$routeParams,$compile,Recipes){
$scope.recipes.$loaded().then(函数(有效负载){
$scope.recipe=payload.$getRecord($routeParams.id);
$scope.html=$scope.recipe.instructions;
if(typeof$scope.recipe.reviews==“未定义”){
$scope.recipe.reviews=[{}];
}
$scope.calculateAverage=函数(平均数据){
var总和=0;
如果($scope.recipe.reviews.length>1){
对于(变量i=1;i<$scope.recipe.reviews.length;i++){
sum+=parseInt($scope.recipe.reviews[i].stars,10);
}
var average=sum/($scope.recipe.reviews.length-1);
var roundedAverage=数学四舍五入(平均值);
返回{
平均值:四舍五入平均值,
markedStars:新阵列(舍入平均值)
};
}否则{
回报金额;
}
};
});
]);

在您的
RecipeController
定义中,您立即调用:

$scope.recipes.$loaded().then(function(payload) { ... }
…假设定义了
$scope.recipes
,并且其属性为
$loaded
——情况并非如此

在您的测试中:

describe("$scope.calculateAverage", function() {
  it("calculates average correctly", function() {
    var $scope = {};
    var controller = $controller('RecipeController', { $scope: $scope });

    $scope.calculateAverage();

    expect(average).toBe(sum/(Recipes.reviews.length-1));
  });
});
…将作用域定义为空对象,然后将其注入控制器

假设您使用Jasmine作为测试框架,您可以这样:

var $scope = {
    recipes: {
        $loaded: function() { /* this is a mock function */ }
    }
};
var deferred = $q.defer();
deferred.resolve({
    /* this is the data you expect back from $scope.recipes.$loaded */
});
var promise = deferred.promise;
spyOn($scope.recipes, '$loaded').and.returnValue(promise);
这只是您可以删除该函数并控制测试中获得的数据的许多方法之一。它假定您对和有基本的理解

最佳实践

最好不要将数据附加到
$scope
服务。如果您不熟悉,我建议您阅读

TL;DR:控制器只是一个JavaScript“类”,定义函数是它的构造函数。使用
var vm=this;
然后将变量附加到实例引用
vm
(如在“视图模型”中,或您想调用它的任何地方)


不要依赖已在其他地方定义的
$scope.recipes
,您应该在控制器中明确定义它。如果
recipes
是在另一个控制器中定义的,请创建一个两个控制器都可以共享的服务。

是否创建一个数组以从中获取一项?您可以使用
$firebaseObject
来t一项代替,嗨!Shaun谢谢你,tipp!!我一直在尝试你的建议。但我有另一个错误。$scope.calculateAverage()方法不存在。你能给我更多提示吗?非常感谢!!!在你的测试中,$scope只包含你放入的内容。你真正的问题是,你在应用程序中创建了隐式依赖项,方法是在一个位置将内容附加到$scope,并期望它在另一个位置。测试你的控件变得非常困难如果你能重现问题并发布链接,我会很乐意把它交出来,让它正常工作。