多个get请求一个控制器:意外请求,单元测试 - AngularJS

时间:2014-01-13 15:29:46

标签: unit-testing angularjs

我可以找到很多来自控制器的单个http调用的示例以及如何测试它们,但没有多个测试的示例。

我的第一个测试在控制器中没有Product.find(10)的情况下正常工作。当我添加该行时,第一个测试会崩溃。

错误:

Error: Unexpected request: GET 0.0.0.0:3000/api/products No more request expected

Error: Unexpected request: GET 0.0.0.0:3000/api/products No more request expected

我已经尝试了很多东西:包括在每个之前,这给了我一个未定义的错误,我尝试使用expect而不是when,我尝试将两个whens添加到两个测试,以及上面的组合。我显然做了一些非常错误的事情,但作为一个有角度的新手,很难确切地知道那可能是什么,特别是缺少示例..我只是想让我的第一个测试通过Product.find(10)

以下是我的测试:

'use strict';

describe('productsController', function() {

    var scope, $httpBackend;
    var api_root = '0.0.0.0:3000/api/';

    beforeEach(angular.mock.module('sprangularApp'));

    beforeEach(angular.mock.inject( function($rootScope, $controller, _$httpBackend_) {

        $httpBackend = _$httpBackend_;

        //Get mock jsons
        jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';

        scope = $rootScope.$new();
        $controller('productsController', {$scope: scope});
    }));

    //Start Tests
    it('Should be array of all products', function() {
        $httpBackend.when('GET', api_root + 'products').respond(
            getJSONFixture('products.json')
        );

        $httpBackend.flush();
        expect(scope.products[3].name).toBe('Ruby on Rails Bag');
    });

    it('Should instantiate a new product object from json data', function() {
        $httpBackend.when('GET', api_root + 'products/10').respond(
            getJSONFixture('10.json')
        );

        $httpBackend.flush();
        expect(scope.currentProduct.name).toBe('Spree Ringer T-Shirt');
    });
}); 

我正在测试的控制器:

// Generated by CoffeeScript 1.6.3
(function() {
  var sprangularControllers;

  sprangularControllers = angular.module('sprangularControllers', ['sprangularServices']);

  sprangularControllers.controller('productsController', [
    '$scope', 'Product', function($scope, Product) {
      Product.products_with_meta().$promise.then(function(response) {
        return $scope.products = response.products;
      });
      return Product.find(10);
    }
  ]);

}).call(this);

带资源请求的工厂:

sprangularServices = angular.module('sprangularServices', ['ngResource'])

sprangularServices.factory('Defaults', -> 
    api_url: "0.0.0.0:3000/api/"
)

sprangularServices.factory('Product', ($resource, Defaults) -> 
    # $resource(Defaults.api_url + 'products.json')
    class Product
        constructor: ->
            @service = $resource(Defaults.api_url + 'products/:id', {id: '@id'})

        this.products_with_meta = ->
            service = $resource(Defaults.api_url + 'products')
            service.get()

        this.find = (id) -> 
            service = $resource(Defaults.api_url + 'products/:id', {id: id})
            service.get()
)

根据迈克尔的建议,我已经编辑了我的测试,但是我仍然得到完全相同的结果:

'use strict';

describe('productsController', function() {

    var $rootScope, $httpBackend, createController;
    var api_root = '0.0.0.0:3000/api/';

    beforeEach(angular.mock.module('sprangularApp'));

    beforeEach(inject(function($injector) {
        $httpBackend = $injector.get('$httpBackend');

        //Get mock jsons
        jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';

        $rootScope = $injector.get('$rootScope');

        var $controller = $injector.get('$controller');
        createController = function() {
            return $controller('productsController', {'$scope' : $rootScope });
        };
    }));

    afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
    });

    //Start Tests
    it('Should be array of all products', function() {
        $httpBackend.when('GET', api_root + 'products').respond(
            getJSONFixture('products.json')
        );

        var controller = createController();

        $httpBackend.flush();
        expect($rootScope.products[3].name).toBe('Ruby on Rails Bag');
    });

    it('Should instantiate a new product object from json data', function() {
        $httpBackend.when('GET', api_root + 'products/10').respond(
            getJSONFixture('10.json')
        );

        var controller = createController();

        $httpBackend.flush();
        expect($rootScope.currentProduct.name).toBe('Spree Ringer T-Shirt');
    });
}); 

2 个答案:

答案 0 :(得分:2)

以这种方式构建我的测试似乎解决了这个问题:

'use strict';

describe('productsController', function() {

    var $rootScope, $httpBackend, createController;
    var api_root = '0.0.0.0:3000/api/';

    beforeEach(angular.mock.module('sprangularApp'));

    beforeEach(inject(function($injector) {
        $httpBackend = $injector.get('$httpBackend');

        //Get mock jsons
        jasmine.getJSONFixtures().fixturesPath='base/js/tests/api_mock';

        $rootScope = $injector.get('$rootScope');

        var $controller = $injector.get('$controller');
        createController = function() {
            return $controller('productsController', {'$scope' : $rootScope });
        };

        $httpBackend.when('GET', api_root + 'products').respond(
            getJSONFixture('products.json')
        );

        $httpBackend.when('GET', api_root + 'products/10').respond(
            getJSONFixture('10.json')
        );

        var controller = createController();

        $httpBackend.flush();
    }));

    afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
        $httpBackend.resetExpectations();
    });

    //Start Tests
    it('Should be array of all products', function() {
        expect($rootScope.products[3].name).toBe('Ruby on Rails Bag');
    });

    it('Should instantiate a new product object from json data', function() {
        expect($rootScope.currentProduct.name).toBe('Spree Ringer T-Shirt');
    });
}); 

答案 1 :(得分:1)

我认为的顺序定义响应,执行 http调用 flush 并执行测试是不对的。

  1. 定义http调用应如何响应

    $httpBackend.when('GET', api_root + 'products').respond(
            getJSONFixture('products.json')
    );
    
  2. 通过您的代码进行调用

    $controller('productsController', {$scope: scope});
    
  3. 刷新httpBackend(例如模拟$ http的异步行为)

    $httpBackend.flush();
    
  4. 进行测试

    expect(scope.products[3].name).toBe('Ruby on Rails Bag');
    
  5. 因为你的控制器在他的构造函数中进行了后端调用,并且在定义响应应该是什么之前进行了实例化,所以你得到了错误。

    更多信息和例子非常接近您的使用案例:http://docs.angularjs.org/api/ngMock。$ httpBackend