AngularJS 中的单元测试 - 模拟服务和承诺 [英] Unit testing in AngularJS - Mocking Services and Promises

查看:20
本文介绍了AngularJS 中的单元测试 - 模拟服务和承诺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Angular 中,一切似乎都有一个陡峭的学习曲线,并且单元测试 Angular 应用程序绝对无法摆脱这种范式.

In Angular everything seems to have a steep learning curve and unit testing an Angular app definitely doesn't escape this paradigm.

当我开始使用 TDD 和 Angular 时,我觉得我花了两倍(可能更多)的时间来弄清楚如何进行测试,甚至可能更多的是为了正确设置我的测试.但作为 BenNadel 在他的博客中提到了 Angular 学习过程中的起起落落.他的图表绝对是我使用 Angular 的经验.

When I started with TDD and Angular I felt that I was spending twice (maybe more) as much time figuring out just how to test and maybe even more just getting my tests set up correctly. But as Ben Nadel put it in his blog there are ups and downs in the angular learning process. His graph is definitely my experience with Angular.

然而,随着我在学习 Angular 和单元测试方面也取得了进步,现在我觉得我花在设置测试上的时间少了很多,而花更多的时间让测试从红色变为绿色——这是一种很好的感觉.

However as I have progressed in learning Angular and unit testing as well, now i feel that I am spending much less time setting up tests and much more time making tests go from red to green - which is a good feeling.

所以我遇到了设置单元测试来模拟服务和承诺的不同方法,我想我会分享我所学到的知识并提出以下问题:

So I have come across different methods of setting up my unit test to mock services and promises and I thought I would share what I have learned and also ask the question of:

是否有其他或更好的方法来实现这一点?

Are there any other or better ways of accomplishing this?

所以说到代码,无论如何我们都来这里 - 不要听某个人谈论他的爱,错误地学习框架的成就.

So onto the code, that what we all come for here anyways - not to listen to some guy talk about his love, err accomplishments learning a framework.

这就是我开始模拟我的服务和承诺的方式,我将使用控制器,但显然可以在其他地方模拟服务和承诺.

This is how I started out mocking my services and promises, I'll use a controller, but services and promises can be mocked in other places obviously.

describe('Controller: Products', function () {
    var//iable declarations
        $scope,
        $rootScope,
        ProductsMock = {
            getProducts: function () {
            } // There might be other methods as well but I'll stick to one for the sake of consiseness
        },
        PRODUCTS = [{},{},{}]
    ;

    beforeEach(function () {
        module('App.Controllers.Products');
    });

    beforeEach(inject(function ($controller, _$rootScope_) {
        //Set up our mocked promise
        var promise = { then: jasmine.createSpy() };

        //Set up our scope
        $rootScope = _$rootScope_;
        $scope = $rootScope.$new();

        //Set up our spies
        spyOn(ProductsMock, 'getProducts').andReturn(promise);

        //Initialize the controller
        $controller('ProductsController', {
            $scope: $scope,
            Products: ProductsMock
        });

        //Resolve the promise
        promise.then.mostRecentCall.args[0](PRODUCTS);

    }));

    describe('Some Functionality', function () {
        it('should do some stuff', function () {
            expect('Stuff to happen');
        });
    });
});

对我们来说这是有效的,但随着时间的推移,我认为一定有更好的方法.对于一个我讨厌

For us this worked, but as time went on I thought there must be a better way. For one I hated the

promise.then.mostRecentCall 

thing,如果我们想重新初始化控制器,那么我们必须将它从 beforeEach 块中拉出来,并将其单独注入每个测试中.

thing, and if we wanted to reinitialise the controller then we had to pull it out of the beforeEach block and inject it individually into each test.

必须有更好的方法...

There has to be a better way...

现在我问有没有人有其他方法来设置测试,或者对我选择的方法有什么想法或感受?

Now I ask does anyone have other ways to set tests up, or and thoughts or feeling on the way I have chose to do it?

推荐答案

然后我遇到了另一个帖子、博客、stackoverflow 示例(你选择它我可能在那里),我看到了 $q 库的使用.呸!当我们可以使用 Angular 给我们的工具时,为什么要设置一个完整的模拟承诺.我们的代码看起来更漂亮,看起来更有意义 - 没有丑陋的 promise.then.mostRecent thing.

Then I came across another post, blog, stackoverflow example (you pick it I was probably there), and I saw the use of the $q library. Duh! Why set up a whole mock promise when we can just use the tool that Angular gives us. Our code looks nicer and makes more sense to look at - no ugly promise.then.mostRecent thing.

接下来的单元测试迭代是这样的:

Next in the iteration of unit testing was this:

describe('Controller: Products', function () {
    var//iable declarations
        $scope,
        $rootScope,
        $q,
        $controller,
        productService,
        PROMISE = {
            resolve: true,
            reject: false
        },
        PRODUCTS = [{},{},{}] //constant for the products that are returned by the service
    ;

    beforeEach(function () {
        module('App.Controllers.Products');
        module('App.Services.Products');
    });


    beforeEach(inject(function (_$controller_, _$rootScope_, _$q_, _products_) {
        $rootScope = _$rootScope_;
        $q = _$q_;
        $controller = _$controller_;
        productService = _products_;
        $scope = $rootScope.$new();
    }));

    function setupController(product, resolve) {
        //Need a function so we can setup different instances of the controller
        var getProducts = $q.defer();

        //Set up our spies
        spyOn(products, 'getProducts').andReturn(getProducts.promise);

        //Initialise the controller
        $controller('ProductsController', {
            $scope: $scope,
            products: productService
        });

        // Use $scope.$apply() to get the promise to resolve on nextTick().
        // Angular only resolves promises following a digest cycle,
        // so we manually fire one off to get the promise to resolve.
        if(resolve) {
            $scope.$apply(function() {
                getProducts.resolve();
            });
        } else {
            $scope.$apply(function() {
                getProducts.reject();
            });
        }
    }

    describe('Resolving and Rejecting the Promise', function () {
        it('should return the first PRODUCT when the promise is resolved', function () {
            setupController(PRODUCTS[0], PROMISE.resolve); // Set up our controller to return the first product and resolve the promise. 
            expect('to return the first PRODUCT when the promise is resolved');
        });

        it('should return nothing when the promise is rejected', function () {
            setupController(PRODUCTS[0], PROMISE.reject); // Set up our controller to return first product, but not to resolve the promise. 
            expect('to return nothing when the promise is rejected');
        });
    });
});

这开始感觉应该是这样设置的.我们可以嘲笑我们需要嘲笑的东西,我们可以设定解决和拒绝的承诺,这样我们就可以真正测试两种可能的结果.这个感觉不错...

This started to feel like the way it should be set up. We can mock what we need to mock we can set our promise to resolve and reject so we can truly test the two possible outcomes. This feels good...

这篇关于AngularJS 中的单元测试 - 模拟服务和承诺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆