简体   繁体   中英

How to test my http request in my app

I am trying to write units test for my app and I have the following issue

In my controller, I have something like

 $scope.test1 = function() {
     productFactory.getName()
         .then(function(products){
             $scope.result = products;
          })
} 

productFactory

angular.module('myApp').factory('productFactory', function($http) {
    var factoryObj = {};
    factoryObj.getName = function() {
        return http.get(url)
    }

    return factoryObj
})

In my unit test file

describe('test here', function () {
    var testCtrl, scope, httpBackend, mockFactory;

    beforeEach(module('myApp', function($provide){
        $provide.value('productFactory', mockFactory);
    }));

    // Initialize the controller and a mock scope
    beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_,  _productFactory_) {
        scope = _$rootScope_.$new();
        httpBackend = _$httpBackend_;
        mockFactory = _productFactory_;

        testCtrl = _$controller_('testCtrl', {
            $scope: scope
        });

    it('should get product name', function() {       
        scope.test1();
        //I am not sure how to test the results
    });
}));

When I run karma test, it gives me

TypeError: 'undefined' is not an object (evaluating 'productFactory.getName()')

I am not sure how to test the http result and fix the error. Can anyone help me about it? Thanks a lot!

First of all, you don't need to worry about using $provide :

beforeEach(module('myApp'));

1. Without $httpBackend (mock out the service completely)

Then, productFactory will be passed into your controller, but you want to spyOn the getName() :

// Initialize the controller and a mock scope
beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_,  _productFactory_) {
    scope = _$rootScope_.$new();
    httpBackend = _$httpBackend_;
    mockFactory = _productFactory_;

    // add spy for the method, wrap with $q.when so it returns a promise
    spyOn(mockFactory, 'getName').and.returnValue($q.when('Pizza!'));

    testCtrl = _$controller_('testCtrl', {
        $scope: scope,
        productFactory: mockFactory  // pass in here
    });

Then, you've got to cause a $digest cycle, so that the promise will call through:

it('should get product name', function() {       
    scope.test1();

    // hit the $digest        
    scope.$apply();

    // expectation
    expect(scope.result).toBe('Pizza!')
});

2. With $httpBackend

// Initialize the controller and a mock scope
    beforeEach(inject(function (_$controller_, _$rootScope_, _$httpBackend_) {
        scope = _$rootScope_.$new();
        httpBackend = _$httpBackend_;

        // set up httpBackent
        httpBackend.when('GET', '/products')
                            .respond([{ name: 'Pizza!'}, {name: 'Sandwich'}]);

        testCtrl = _$controller_('testCtrl', {
            $scope: scope
        });

We don't need to mock the factory in this case at all. Then, we just need to flush $httpBackend when we want the http call to return:

it('should get product name', function() {       
    scope.test1();

    // hit the $digest with flush        
    httpBackend.flush();

    // expectation
    expect(scope.result.length).toBe(2)
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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