4

Создание запроса, который не высмеивал с $httpBackend.when в угловых 1.x блоке/результаты интеграции тестов с ошибкой:Создание реальных запросов HTTP-сервера в блоке AngularJS/интеграция тестов

Error: Unexpected request: GET /real-request

Можно ли сделать настоящие HTTP-запросы с ngMock и тестовой установкой Karma + Jasmine? Что такое хорошая практика?

+0

Пожалуйста, объясните, что такое «настоящий» HTTP-запрос в вашем случае? – lin

+0

Настоящий запрос - это запрос, который вызывает window.XMLHttpRequest и выполняет запрос на HTTP-сервер. Это не допускается конструкцией в Угловых модульных тестах. – estus

+0

@lin Это автоответчик, кстати. Я считаю эту печально известную проблему. – estus

ответ

5

AngularJS - это самоуверенная структура, и ее мнение по HTTP-запросам в модульных тестах заключается в том, что все они должны быть издевались.

Не рекомендуется выполнять настоящие HTTP-запросы в модульных тестах по двум причинам. Модульные тесты должны быть изолированы и быть быстрыми. Выполнение реального запроса делает тест асинхронным, что значительно замедляет выполнение тестов. Выполнение реального запроса нарушает изоляцию, факт прохождения теста зависит как от тестируемой единицы, так и от бэкэнд.

Это было учтено, когда был разработан модуль AngularJS ngMock (он автоматически загружается в модульные тесты с помощью углового mocks.js). Разработчик вряд ли когда-либо сделает asynchronous Жасмин модульные тесты с угловым, потому что нет необходимости делать это.

Тесты интеграции отличаются. Они могут быть не такими широкими, как тесты E2E (которые часто выполняются Protractor) и проверять, как несколько модулей работают вместе, это может включать бэкэнд (HTTP-сервер). Таким образом, в конце Карма и Жасмин все еще используются, но тесты могут быть медленнее и асинхронны и выполнять реальные HTTP-запросы.

Здесь находится модуль ngMockE2E (обычно используется в тестах E2E). Он включен в угловое mocks.js вместе с ngMock, но по умолчанию не загружен.

The ngMockE2E is an AngularJS module which contains mocks suitable for end-to-end testing. Currently there is only one mock present in this module - the e2e $httpBackend mock.

ngMockE2E содержит различные $httpBackend реализации, которые могут быть использованы для этой цели. Его API меняется. Не предполагается использовать методы flush и extend. $rootScope.$digest() может использоваться, если есть цепочки обещаний $q, которые должны быть выполнены.

ngMockE2E не будет работать из коробки должным образом из-за корректировки, которые делаются для угловых услуг ngMock когда его вспомогательные функции module и inject используются. Вспомогательный модуль для ИНТЕГРАЦИИ тестов можно использовать вместо:

angular.module('ngMockI9n', []).config(function ($provide) { 
    // hack to restore original implementations which were overridden by ngMock 
    angular.injector(['ng', function ($httpBackendProvider, $browserProvider) { 
    $provide.provider('$httpBackend', $httpBackendProvider); 
    $provide.provider('$browserI9n', $browserProvider); 
    }]); 

    // make ngMockE2E $httpBackend use original $browser 
    var httpBackendI9nDecorator = angular.mock.e2e.$httpBackendDecorator 
    .map(function (dep) { 
    return (dep === '$browser') ? '$browserI9n' : dep; 
    }); 
    $provide.decorator('$httpBackend', httpBackendI9nDecorator); 
}); 

Кроме того, рецепт whitelisted real HTTP requests может быть использован для тестирования проще, хотя лучше практика перечислить реальные и издевались запросы в явном виде.

beforeEach(module('app')); 
beforeEach(module('ngMockI9n')); 

beforeEach(inject(function ($httpBackend) { 
    $httpBackend.when('GET', '/mocked-request').respond(200, {}); 

    // all other requests will be automatically whitelisted and treated as real 
    // so make sure that mocked requests are mocked above this point 
    angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD', 'PUT', 'POST', 'PATCH'], 
    function (method) { 
    $httpBackend.when(method).passThrough(); 
    }); 
})); 


it('does real async request', function (done) { 
    // async tests need extra `done` param 
    inject(function() { 
    $http.get('real-request').then(function (response) { 
     expect(response.data).toEqual(...); 
    }) 
    .then(done, done.fail); 

    $rootScope.$digest(); 
    }); 
}); 

it('does mocked sync request', function (done) { 
    // tests with mocked requests are async, too 
    inject(function() { 
    $http.get('mocked-request').then(function (response) { 
     expect(response.data).toEqual(...); 
    }) 
    .then(done, done.fail); 

    $rootScope.$digest(); 
    }); 
}); 

TL; DR: Использование $httpBackend из ngMockE2E в интеграционных тестов для реальных запросов, это требует некоторой дополнительной работы, чтобы сделать его совместимым с ngMock. Никогда не делайте настоящих запросов в модульных тестах, это приводит к медленным и мутным испытаниям.