Подтвердить что ты не робот

E2E mock $httpBackend на самом деле не пропускает для меня

Хотя я считаю, что следую инструкциям здесь для настройки $httpBackend для передачи выбранных запросов на сервер, он не работает для меня.

Вот Plunkr с неудачным тестом, который показывает, что я делаю, и объясняет в комментариях, что кажется неправильным.

Мое заклинание предполагает, что по какой-то причине макет $httpBackend не имеет внутренней копии реального $httpBackend, так что, когда приходит время пройти через запрос XHR, он передает его в макет $httpBackend. Этот второй вызов выдает исключение, потому что он не знает, что делать с запросом.

Ответ на dtabuenc

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

Я не думаю, что ты вообще злой. Ваш ответ совершенно разумный... или он был бы разумным, если бы он не противоречил тексту ссылки API/ngMockE2E/$httpBackend". Я цитирую:

Эта реализация может использоваться для ответа статическими или динамическими ответами с помощью when api и его ярлыков (whenGET, whenPOST и т.д.) и необязательно передавать запросы реальному $httpBackend для (например, для взаимодействия с определенным удаленным apis или для получения шаблонов с веб-сервера)...

[I] n сценарий сквозного тестирования или сценарий, когда приложение разрабатывается с заменой реального бэкэнда api на макет, часто желательно, чтобы определенная категория запросов обходила макет и выдать реальный http-запрос. Чтобы настроить бэкэнд с этим поведением, используйте обработчик запроса passThrough, если вместо respond. [emphasis mine].

В документации не говорится об использовании E2E $httpBackend в среде Jasmine. Я не могу придумать причины, чтобы исключить его. Если есть такая причина, они должны четко заявить об этом. Серьезно, кто читает о макетном компоненте и не ожидает его использования в тестовой среде?

Чтобы "передавать запросы к реальному $httpBackend для определенных запросов, например, взаимодействовать с определенным удаленным apis", именно то, что я намереваюсь сделать. Что они могут иметь в виду под "реальным $httpBackend", кроме не-макетной версии этого компонента?

Я не понимаю ваших утверждений о том, что

Модуль ngMocksE2E предназначен для использования на стороне сервера, где выполняется фактическое приложение angular.

Слово "сервер" отображается ровно 3 раза на этой странице, ни разу не предлагая, чтобы какой-либо код приложения выполнялся на "сервере". Я не знаю, что вы подразумеваете под "фактическим приложением angular", выполняющим "сторону сервера".

В документации совершенно ясно, что E2E $httpBackend не ограничивается тестированием E2E. Это также относится к "сценарию, когда приложение разрабатывается с заменой реального бэкэнда api на макет".

Это на полшага от моего сценария, в котором приложение проверено с реальным backend api. "

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

Конечно, я могу протестировать (и сделать тест) с ложными ответами XHR, что способность компонента правильно реагировать на то, что я предсказываю, будет бэкэндом. Это не то же самое, что проверка того, что компонент отвечает соответствующим образом действительному бэкэнд-поведению... который может измениться по мере того, как приложение развивается и отходит от посмеиваемых ответов каким-то значительным образом.

Я бы подумал о том, чтобы использовать ваш тестер на полпути для этой цели, если я понял, как поменять его на код кода SUT. Я не. Я думаю, что компонент, создающий запросы XHR, недоступен для вашего ngMidwayTester. Но я знаю, как замять реальный помощник XHR в конвейер, если мне нужно.

Здесь я нахожусь в данный момент.

Либо кто-то может показать, как сделать $httpBackend передавать определенные запросы на сервер - как заявляет документация, - или я сам заменит реализацию passThrough на рабочую реализацию XHR.

Я предпочитаю первый вариант. Если перейти ко второму, я предложу ссылку на него здесь для других, которые разделяют мои потребности и мою интерпретацию документации API.

Есть ли третий способ, который мне не хватает?

4b9b3361

Ответ 1

Ниже приведено объяснение цели $httpBackend, которая находится в модуле ngMockE2E.

Модуль ngMockE2E просто не предназначен и не предназначен для использования из спецификации жасмина.

При проведении сквозного тестирования на тестирование присутствуют две стороны. Один из них - это приложение angular, которое тестируется, другое - код angular -scenario, который находится в спецификации Jasmine.

В тестах E2E нет модуля angular, или ng-mocks, или чего-либо angular, связанного на стороне жасминов (кроме сценария).

Модуль ngMocksE2E предназначен для использования на стороне сервера, где выполняется фактическое приложение angular. Основная цель - предоставить нам предварительные ответы, чтобы тестирование интерфейса на уровне интеграции могло протекать намного быстрее, чем если бы каждая страница фактически отправилась на сервер для JSON.

При использовании жасмина вместе с ng-mocks, angular всегда будет заменять $httpBackend макетом. При добавлении модуля ngMocksE2E он не сможет получить какой-либо "реальный" $httpBackend, и, как вы уже узнали, будет просто обертывать макет и делегировать его на прохождение.

Казалось бы, тип теста, который вы пытаетесь написать, - это тест, который не тестирует интеграцию с пользовательским интерфейсом, а проверяет интеграцию с javascript и сервером приложения.

Это совершенно законный стиль тестирования (в качестве "midwayTesting" в сообществе angular упоминается как "midwayTesting" ). Ваша проблема в том, что вы используете неправильный инструмент.

Я бы посмотрел на это:

https://github.com/yearofmoo/ngMidwayTester

Что вы использовали бы вместо angular -mocks и angular.module(), чтобы облегчить те виды тестирования, которые, как я предполагаю, вы хотите сделать.

Подробнее об этом можно прочитать здесь:

http://www.yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-karma.html

(извинения, если вы уже были связаны там)

EDIT: (для просмотра дополнительных комментариев)

У вас есть настоящая говядина в том, что в документации не ясно, что ngMockE2E не может использоваться на стороне клиента (т.е. карма/жасмин) от сквозной установки тестирования. Неразумно интерпретировать такие вещи, как вы их интерпретировали, но это не меняет того факта, что интерпретация неверна.

ngMockE2E будет проходить через запросы, если это указано при использовании на стороне сервера приложения, а не на стороне клиента. Это означает, что вы все равно можете пройти через определенные запросы, которые сложно издеваться над заранее подготовленными ответами. То, что я подразумеваю под клиентом и на стороне сервера, - это то, что при сквозном тестировании есть два конца. У вас есть приложение для тестирования, которое обслуживается стандартным сервером приложений, и у вас есть тестовый код, который управляет приложением, обычно выполняемым в карме или другом тестовом бегуне, которое использует стандартные HTTP-запросы для связи с приложением, которое выполняется в другой процесс.

Если вы посмотрите на документацию и как настроить ngMockE2E, вы заметите, что Jasmine не упоминается, а инструкции для настройки в реальном приложении angular:

myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
myAppDev.run(function($httpBackend) {
  phones = [{name: 'phone1'}, {name: 'phone2'}];

  // returns the current list of phones
  $httpBackend.whenGET('/phones').respond(phones);

  // adds a new phone to the phones array
  $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
    phones.push(angular.fromJson(data));
  });
  $httpBackend.whenGET(/^\/templates\//).passThrough();
  //...
});

Как вы можете видеть в этом примере, они издеваются над всеми инструкциями данных JSON, давая возможность получать шаблоны с сервера.

Чтобы использовать его из жасмина, настройка была бы совсем другой, используя angular.mock.module('ngMockE2E'), а затем настроив $httpBackend.whenGET() на beforeEach(), а не на module.run().

Насколько я был связан с ngMidwayTester, я считаю, что это действительно совместимо с ngMockE2E. По существу ngMidwayTester заменяет angular.mock.module() и inject() своими собственными реализациями. Поэтому вы можете использовать его следующим образом:

beforeEach(function(){
  tester = ngMidwayTester('app', 'ngMockE2E');
  $http = tester.inject('$http');
  $httpBackend = tester.inject('$httpBackend');
  $rootScope = tester.inject('$rootScope');
});

Это должно работать, потому что вы больше не используете модуль ngMock (который всегда включается, когда вы используете angular.mock.module()). Все должно работать точно так же, как вы хотите, чтобы они использовали ngMidwayTester.

Ответ 2

Я наткнулся на ту же проблему, но вместо внедрения богатого API или замены исходных angular -моков, просто добавленных в следующем помощнике:

angular.module('httpReal', ['ng'])
    .config(['$provide', function($provide) {
        $provide.decorator('$httpBackend', function() {
            return angular.injector(['ng']).get('$httpBackend');
        });
    }])
    .service('httpReal', ['$rootScope', function($rootScope) {
        this.submit = function() {
            $rootScope.$digest();
        };
    }]);

Он исправляет две проблемы, которые препятствуют прохождению HTTP-запроса:

  • Восстанавливает оригинальный $httpBackend;

  • Предоставляет метод для выполнения запросов, так как иначе они будут в очереди AngularJS, ожидающей цикла дайджеста.

describe('my service', function() {
    var myService, httpReal;

    beforeEach(module('myModule', 'httpReal'));

    beforeEach(inject(function( _myService_, _httpReal_ ) {
        myService = _myService_;
        httpReal = _httpReal_;
    }));

    it('should return valid data', function(done) {
        myService.remoteCall().then(
            function(data) {
                expect(data).toBeDefined();
                done();
            }, function(error) {
                expect(false).toBeTruthy();
                done();
            });

        httpReal.submit();
    });
});

Ответ 3

Чтобы протестировать мое приложение с помощью реальных бэкэндов, я использовал измененную версию angular-mocks

Он работает так же, как и для юнит-тестов в Жасмине.

Я использую его с Jasmine 2.0, поэтому тест выглядит следующим образом:

it(' myTest', function (done) {
    _myService.apiCall()
        .then(function () {
            expect(true).toBeTruthy();
            done()
        });
});

Примечание: требуется done из-за асинхронного вызова.

Ответ 4

Вот решение, которое я использую для создания настоящих HTTP-вызовов, когда я использую ngMock для модульных тестов. Я в основном использую его для отладки, тестирования API, получения JSON-примеров и т.д.

Я написал более подробное сообщение о решении в своем блоге: Как Unit Test с реальными HTTP-вызовами с помощью ngMockE2E и passThrough.

Решение выглядит следующим образом:

angular.mock.http = {};

angular.mock.http.init = function() {

  angular.module('ngMock', ['ng', 'ngMockE2E']).provider({
    $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
    $log: angular.mock.$LogProvider,
    $interval: angular.mock.$IntervalProvider,
    $rootElement: angular.mock.$RootElementProvider
  }).config(['$provide', function($provide) {
    $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
    $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
    $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
    $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
    $provide.decorator('$controller', angular.mock.$ControllerDecorator);
  }]);

};

angular.mock.http.reset = function() {

  angular.module('ngMock', ['ng']).provider({
    $browser: angular.mock.$BrowserProvider,
    $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
    $log: angular.mock.$LogProvider,
    $interval: angular.mock.$IntervalProvider,
    $httpBackend: angular.mock.$HttpBackendProvider,
    $rootElement: angular.mock.$RootElementProvider
  }).config(['$provide', function($provide) {
    $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
    $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
    $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
    $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
    $provide.decorator('$controller', angular.mock.$ControllerDecorator);
  }]);

};

Включите этот исходный файл после ngMock, например:

<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="angular-mocks.js"></script>
<!-- this would be the source code just provided -->
<script type="text/javascript" src="ngMockHttp.js"></script>

Как написать тест?

  describe('http tests', function () {

    beforeEach(module('moviesApp'));

    var $controller;
    var $httpBackend;
    var $scope;

    describe('real http tests', function() {

      beforeEach(angular.mock.http.init);
      afterEach(angular.mock.http.reset);

      beforeEach(inject(function(_$controller_, _$httpBackend_) {
        $controller = _$controller_;
        $scope = {};
        $httpBackend = _$httpBackend_;

        // Note that this HTTP backend is ngMockE2E's, and will make a real HTTP request
        $httpBackend.whenGET('http://www.omdbapi.com/?s=terminator').passThrough();
      }));

      it('should load default movies (with real http request)', function (done) {
        var moviesController = $controller('MovieController', { $scope: $scope });

        setTimeout(function() {
          expect($scope.movies).not.toEqual([]);
          done();
        }, 1000);

      });

    });

  });

Как это работает?

Он использует версию ngMockE2E $httpBackEndProvider, которая предоставляет нам функцию passThrough, которая, как мы видим, используется в тесте. Это делает, как следует из названия, и пропускает собственный HTTP-вызов.

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

Почему я так делаю?

Мне нравится гибкость, позволяющая легко переключаться между использованием подделок и настоящими HTTP-вызовами, поскольку это помогает моему документообороту при написании тестов, поэтому используется версия ngMockE2E $httpBackEndProvider. Это также позволяет мне кодировать модульные тесты так же, как с помощью ngMock, где я могу просто вставить/исключить строку beforeEach/afterEach для регистрации angular.mock.http.init/reset.

Plunkr

Здесь Plunkr с примером.