简体   繁体   English

Angularjs和Jasmine:使用同一服务的多个实例进行测试

[英]Angularjs and Jasmine : testing with multiple instances of the same service

I'm currently going through the book called 'Mastering Web Application Development with AngularJS' and in one of the examples there is a test called 'Aggregating callbacks'. 我目前正在阅读《用AngularJS掌握Web应用程序开发》一书,在其中一个示例中有一个名为“聚合回调”的测试。

The example I've got the problem with contains the Person object: 我遇到问题的示例包含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);

    };

};

The object called Restaurant: 名为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();

        }

    };

};

and lastly the test for the aggregating callback: 最后是汇总回调的测试:

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']);

});

As you can see the test doesn't show how items are added / injected to the test and I'm new to the concept of TDD in general. 如您所见,测试没有显示如何向测试中添加/注入项目,并且我对TDD的概念一般还是陌生的。

What I've ended up doing was to convert those global objects to service and factory: 我最终要做的是将这些全局对象转换为服务和工厂:

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();

            }

        };

    });

But now I'm struggling with the multiple instances of the service to represent 'pawel' and 'pete' in my test: 但是现在,我在服务的多个实例中苦苦挣扎,无法在测试中表示“ 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']);

    });


});

As I said - I'm new to it and would appreciate some help. 正如我所说-我是新手,希望能有所帮助。

The reason why the tests only allows 测试仅允许的原因

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

is because you have created a Person service. 是因为您创建了个人服务。 In Angular, services are singletons. 在Angular中,服务是单例。 The concept of a 'Person' does not entirely fit with the concept of singletons (ie. there can be more than 1 Person at any given time), but can be used within your application like so: “人员”的概念并不完全与单身人士的概念相适应(即,在任何给定时间可以有1个以上的人员),但可以在您的应用程序中使用,例如:

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

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

   });

You should leave the Person and Restaurant as the book defined them. 您应该按照书中的定义离开人员和餐厅。 I believe that is the book's intention because within that definition there is unique line of code: 我相信这是本书的目的,因为在该定义内有唯一的代码行:

$rootScope.$digest();

http://docs.angularjs.org/guide/concepts This line invokes angular's digest cycle. http://docs.angularjs.org/guide/concepts此行调用了angular的摘要循环。 It basically runs through all that is angular and updates the DOM and View with any changes that occurred within its context. 它基本上遍历所有有角度的内容,并使用其上下文中发生的任何更改来更新DOM和View。 For example, if your html had a binding to a $scope model: 例如,如果您的html绑定到$ scope模型:

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

and after after a few http calls to the server, this model changes within the angular part of the js, angular will automatically update this div to have the new name. 在对服务器进行几次http调用之后,此模型在js的angular部分内更改,angular将自动更新此div以使用新名称。 However, when you have code that exists outside of the angular context, the $digest must be explicitly invoked as angular will not be aware within its context (its angular code), of values that have changed. 但是,当您的代码存在于角度上下文之外时,必须显式调用$ digest,因为angular不会在其上下文(其角度代码)中知道已更改的值。 The only time you might explicitly call $digest() is within directives, but most of the time it deals with 3rd party code's interaction with angular. 您唯一可以显式调用$ digest()的时间是在指令内,但是大多数情况下,它会处理第三方代码与angular的交互。

This would be my suggestion for moving forward: 这是我前进的建议:

Leave the Person and Restaurant code alone. 保留个人和餐厅代码。 Do not convert them into angular services. 不要将它们转换为角度服务。 In a beforeEach function before the aggregating callback test, instantiate 2 Person objects 在聚合回调测试之前的beforeEach函数中,实例化2个Person对象

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

One thing I noticed is that you said that the book's Person and Restaurant were global objects, but they are not. 我注意到的一件事是您说这本书的“人物”和“餐厅”是全局对象,但不是。 they have global functions/constructors. 他们具有全局功能/构造函数。

this should make your tests pass. 这应该使您的测试通过。

Overall, I believe what the book is teaching you through this is to work with code that exists outside of angular. 总的来说,我相信这本书在教给您的是与存在于angular之外的代码一起工作。 In many real-world applications, you barely work with angular js code by itself. 在许多实际的应用程序中,您自己几乎没有使用角度js代码。 Many times you have to work with third-party code such as GoogleMaps, Moment, etc. 很多时候,您必须使用第三方代码,例如GoogleMaps,Moment等。

something to keep in mind for the future: I do believe in the future angular versions (2.0), the project is heading in a direction to abstract the $digest cycle further and use an Observe() fn. 未来需要记住的事情:我确实相信在将来的角度版本(2.0)中,该项目的目标是进一步抽象$ digest循环并使用Observe()fn。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM