AngularJS中的单元测试 - 模拟服务和承诺

Ste*_*how 15 javascript testing unit-testing jasmine angularjs

在Angular中,一切似乎都有一个陡峭的学习曲线,单元测试Angular应用程序肯定不会逃避这种范式.

当我开始使用TDD和Angular时,我觉得我花了两倍(或许更多)时间来确定如何测试,甚至更多只是让我的测试设置正确.但正如Ben Nadel在他的博客中所说,角度学习过程中有起伏.他的图表绝对是我对Angular的体验.

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

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

有没有其他或更好的方法来实现这一目标?

所以在代码上,无论如何我们都来这里 - 不是听一些人谈论他的爱,错误的成就学习框架.

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

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');
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

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

promise.then.mostRecentCall 
Run Code Online (Sandbox Code Playgroud)

事情,如果我们想重新初始化控制器,然后我们不得不将其拉出beforeEach块和单独注射到每个测试.

一定有更好的方法...

现在我问是否有人有其他方法来设置测试,或者我选择这样做的想法或感觉?

Ste*_*how 12

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

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

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');
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

这开始感觉应该像它应该设置的方式.我们可以模拟我们需要模拟的东西,我们可以设定解决和拒绝的承诺,这样我们就可以真正测试两种可能的结果.这感觉很好......