Dreamlines
Dreamlines

Reputation: 380

Testing angular $http inside promise chain with mocha

I have jsdom/mocha/chai set up for backend angular testing.

I have a service that essentially does this (intentionally no post data):

app.service('testService', ['config', '$http', function(config, $http) {
    function getSpecificConfig(type) {
        return config.getConfig()
        .then(function(config) {
            // config is coming back defined;
            // $http timesout
            return $http({method: 'post', url: 'http://localhost:2222/some/path', withCredentials: true});
        })
        .then(function(res) {
            return res.data.config[type];
        })
        .catch(function(err) {
            //handles err
        });
    };

    return {
        getConfig: getConfig
    }
}]);

my test is:

/* jshint node: true */
/* jshint esversion: 6 */

let helpers = require(bootstrapTest),
    inject = helpers.inject,
    config,
    specificConfig,
    mockResponse,
    $httpBackend,
    $rootScope;

//config service
require('config.js');

//testService I'm testing
require('testService');

beforeEach(inject(function($injector, _$httpBackend_) {
    config = $injector.get('config');
    specificConfig = $injector.get('testService');
    $rootScope = $injector.get('$rootScope');
    $httpBackend = _$httpBackend_;

    $httpBackend.when('POST', 'http://localhost:2222/some/path')
    .response(function(data) {
        //would like this to fire
        console.log('something happened');
        mockResponse = {data: 'some data'};
        return mockResponse;
    });
}));   

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

describe ('this service', function() {
    beforeEach(function() {
        $httpBackend.expect('POST', 'http://localhost:2222/some/path');
        $rootScope.$apply(function() {
            return specificConfig('something');
        });
    });

    it ('returns the specific config', function() {
        expect(mockResponse).to.equal('some data');
    })
});

Problem: When the test is run, the config.getConfig() is resolving properly but the $http leads to a mocha timeout (2000ms) and the afterEach hook throws an Unsatisfied request.

My understanding of this may be completely incorrect so please feel free to educate me on the correct approach (here was my approach):

1) require all necessary dependencies.

2)inject them and set up a $httpBackend listener which fires the test response when the real http is fired.

3) $rootScope.$apply() any promises as the resolution of them is tied to the angular lifecycle.

4) the first before each sets the listener, the second before each fires the service which fires the $http allowing $httpBackend to fire and set the mockResponse.

5) test mock response.

Upvotes: 0

Views: 462

Answers (1)

Assaf Moldavsky
Assaf Moldavsky

Reputation: 1721

If you need to return promises in your mocked HTTP requests you can use angular-mocks-async like so:

var app = ng.module( 'mockApp', [
    'ngMockE2E',
    'ngMockE2EAsync'
]);

app.run( [ '$httpBackend', '$q', function( $httpBackend, $q ) {

    $httpBackend.whenAsync(
        'GET',
        new RegExp( 'http://api.example.com/user/.+$' )
    ).respond( function( method, url, data, config ) {

        var re = /.*\/user\/(\w+)/;
        var userId = parseInt(url.replace(re, '$1'), 10);

        var response = $q.defer();

        setTimeout( function() {

            var data = {
                userId: userId
            };
            response.resolve( [ 200, "mock response", data ] );

        }, 1000 );

        return response.promise;

    });

}]);

Upvotes: 0

Related Questions