1

我目前正在阅读名为“使用 AngularJS 掌握 Web 应用程序开发”的书,其中一个示例中有一个名为“聚合回调”的测试。

我遇到问题的示例包含 Person 对象:

var Person = function(name, $log) {

    this.eat = function(food) {

        $log.info(name + ' is eating delicious ' + food);

    };

    this.beHungry = function(reason) {

        $log.warn(name + ' is hungry because: ' + reason);

    };

};

名为 Restaurant 的对象:

var Restaurant = function($q, $rootScope) {

    var currentOrder;

    return {

        takeOrder : function(orderedItems) {

            currentOrder = {

                deferred : $q.defer(),
                items : orderedItems

            };

            return currentOrder.deferred.promise;

        },

        deliverOrder : function() {

            currentOrder.deferred.resolve(currentOrder.items);
            $rootScope.$digest();

        },

        problemWithOrder : function(reason) {

            currentOrder.deferred.reject(reason);
            $rootScope.$digest();

        }

    };

};

最后是聚合回调的测试:

it('should allow callbacks aggregation', function() {

    var pizzaPid = new Restaurant($q, $rootScope);

    var pizzaDelivered = pizzaPid.takeOrder('Margherita');

    pizzaDelivered.then(pawel.eat, pawel.beHungry);
    pizzaDelivered.then(pete.eat, pete.beHungry);

    pizzaPid.deliveryOrder();

    expect($log.info.logs).toContain(['Pawel is eating delicious Margherita']);
    expect($log.info.logs).toContain(['Pete is eating delicious Margherita']);

});

正如您所看到的,测试没有显示项目是如何添加/注入到测试中的,而且我对 TDD 的一般概念还是陌生的。

我最终所做的是将这些全局对象转换为服务和工厂:

angular.module('myApp', [])

    .service('Person', function(personName, $log) {

        this.eat = function(food) {

            $log.info(personName + ' is eating delicious ' + food);

        };

        this.beHungry = function(reason) {

            $log.warn(personName + ' is hungry because: ' + reason);

        };

    })

    .factory('Restaurant', function($q, $rootScope) {

        var currentOrder;

        return {

            takeOrder : function(orderedItems) {

                currentOrder = {

                    deferred : $q.defer(),
                    items : orderedItems

                };

                return currentOrder.deferred.promise;

            },

            deliverOrder : function() {

                currentOrder.deferred.resolve(currentOrder.items);
                $rootScope.$digest();

            },

            problemWithOrder : function(reason) {

                currentOrder.deferred.reject(reason);
                $rootScope.$digest();

            }

        };

    });

但是现在我正在努力使用服务的多个实例来代表我的测试中的“pawel”和“pete”:

describe('Person and Restaurant tests', function() {

    var Person;
    var Restaurant;

    var $q;
    var $rootScope;
    var $log;




    beforeEach(function() {


        module('myApp');

        module(function($provide) {

            $provide.value('personName', 'Pawel');

        });

        inject(function(_Person_, _Restaurant_, _$q_, _$rootScope_, _$log_) {

            Person = _Person_;
            Restaurant = _Restaurant_;

            $q = _$q_;
            $rootScope = _$rootScope_;
            $log = _$log_;

        });



    });


    it('should allow callbacks aggregation', function() {

        var pizzaDelivered = Restaurant.takeOrder('Margherita');

        // here's where the problem is
        // with current set up I can only call it as
        // pizzaDelivered.then(Person.eat, Person.beHungry);        
        pizzaDelivered.then(pawel.eat, pawel.beHungry);
        pizzaDelivered.then(pete.eat, pete.beHungry);

        Restaurant.deliveryOrder();

        expect($log.info.logs).toContain(['Pawel is eating delicious Margherita']);
        expect($log.info.logs).toContain(['Pete is eating delicious Margherita']);

    });


});

正如我所说 - 我是新手,希望能得到一些帮助。

4

1 回答 1

1

测试只允许的原因

pizzaDelivered.then(Person.eat, Person.beHungry)

是因为你创建了一个 Person 服务。在 Angular 中,服务是单例的。'Person' 的概念并不完全符合单例的概念(即,在任何给定时间可以有超过 1 个人),但可以在您的应用程序中使用,如下所示:

app = app.module('app', [])
  .controller('chicago', function($scope, $log) {

      $scope.family = [
         new Person('henry', $log),
         new Person('me', $log),
         new Person('you', $log)
       ];

   });

您应该按照书中的定义保留 Person 和 Restaurant。我相信这就是本书的意图,因为在该定义中存在独特的代码行:

$rootScope.$digest();

http://docs.angularjs.org/guide/concepts 这一行调用了 Angular 的摘要循环。它基本上贯穿所有有角度的内容,并使用其上下文中发生的任何更改来更新 DOM 和视图。例如,如果您的 html 绑定到 $scope 模型:

<div ng-model='name'> {{ name }} </div>

在对服务器进行几次 http 调用后,此模型在 js 的 angular 部分内发生变化,angular 会自动更新此 div 以具有新名称。但是,当您的代码存在于 Angular 上下文之外时,必须显式调用 $digest,因为 Angular 在其上下文(其角度代码)中不会知道已更改的值。唯一一次可以显式调用 $digest() 是在指令中,但大多数时候它处理 3rd 方代码与 angular 的交互。

这将是我前进的建议:

不理会人员和餐厅代码。不要将它们转换为角度服务。在聚合回调测试之前的 beforeEach 函数中,实例化 2 个 Person 对象

beforeEach(function() {    
    pete = new Person('pete', $log);
    pawel = new Person('pawel', $log);
});

我注意到的一件事是,您说本书的 Person 和 Restaurant 是全局对象,但它们不是。它们具有全局函数/构造函数。

这应该使您的测试通过。

总的来说,我相信这本书教给你的是使用 Angular 之外的代码。在许多实际应用程序中,您几乎不会单独使用 Angular js 代码。很多时候,您必须使用第三方代码,例如 GoogleMaps、Moment 等。

未来要记住的事情:我确实相信未来的 Angular 版本(2.0),该项目正朝着进一步抽象 $digest 循环并使用 Observe() fn 的方向发展。

于 2013-10-12T18:16:39.827 回答