Макет $httpBackend в угловых тестах e2e

У кого-нибудь есть идеи, как смоделировать $httpBackend в тестах angular e2e? Идея заключается в том, чтобы блокировать запросы XHR при выполнении тестов на travis-ci. Я использую карму для прокси-ресурсов и партиалов из моего приложения rails, работающего на travis. Я хочу сделать приемочное тестирование без реальных запросов к БД.

Вот часть моего файла конфигурации кармы:

...
files = [
  MOCHA,
  MOCHA_ADAPTER,

  'spec/javascripts/support/angular-scenario.js',
  ANGULAR_SCENARIO_ADAPTER,

  'spec/javascripts/support/angular-mocks.js',
  'spec/javascripts/e2e/**/*_spec.*'
];
...

proxies = {
  '/app': 'http://localhost:3000/',
  '/assets': 'http://localhost:3000/assets/'
};
...

Вот часть моего spec-файла:

beforeEach(inject(function($injector){
  browser().navigateTo('/app');
}));

it('should do smth', inject(function($rootScope, $injector){
  input('<model name>').enter('smth');
  //this is the point where I want to stub real http query
  pause();
}));

Я пытался получить сервис $httpBackend через $ инжектор:

$injector.get('$httpBackend')

Но это не тот, который используется внутри iframe, где выполняются мои тесты.

Следующая попытка, которую я сделал, была с использованием angular.scenario.dsl, вот пример кода:

angular.scenario.dsl('mockHttpGet', function(){
  return function(path, fakeResponse){
    return this.addFutureAction("Mocking response", function($window, $document, done) {
      // I have access to window and document instances 
      // from iframe where my tests run here
      var $httpBackend =  $document.injector().get(['$httpBackend']);
      $httpBackend.expectGET(path).respond(fakeResponse)
      done(null);
    });
  };
});

Пример использования:

it('should do smth', inject(function($rootScope, $injector){
  mockHttpGet('<path>', { /* fake data */ });
  input('search.name').enter('mow');
  pause();
}));

Это приводит к следующей ошибке:

<$httpBackend listing>  has no method 'expectGET'

Итак, на данный момент я понятия не имею о следующем шаге. Кто-нибудь пробовал делать что-то подобное, действительно ли этот тип заглушки возможен?

3 ответа

Если вы действительно пытаетесь смоделировать бэкэнд в тесте E2E (эти тесты называются сценариями, а спецификации используются для модульного тестирования), то это то, что я делал в проекте, над которым я работал ранее.

Приложение, которое я тестировал, называлось studentsApp, Это было приложение для поиска студентов по запросу API REST. Я хотел протестировать приложение, не запрашивая API.

Я создал приложение E2E под названием studentsAppDev что я вкалываю studentsApp а также ngMockE2E в. Там я определяю, какие вызовы должен ожидать mockBackend и какие данные возвращать. Ниже приведен пример моего studentsAppDev файл:

"use strict";

// This application is to mock out the backend. 
var studentsAppDev = angular.module('studentsAppDev', ['studentsApp', 'ngMockE2E']);
studentsAppDev.run(function ($httpBackend) {

    // Allow all calls not to the API to pass through normally
    $httpBackend.whenGET('students/index.html').passThrough();

    var baseApiUrl = 'http://localhost:19357/api/v1/';
    var axelStudent = {
        Education: [{...}],
        Person: {...}
    };
    var femaleStudent = {
        Education: [{...}],
        Person: {...}
    };
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&')
        .respond([axelStudent, femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axel&')    
        .respond([axelStudent, femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=1&')
        .respond([axelStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=2&')
        .respond([femaleStudent]);
    $httpBackend.whenGET(baseApiUrl + 'students/?searchString=axe&department=3&')    
        .respond([]);

    ...

    $httpBackend.whenGET(baseApiUrl + 'departments/?teachingOnly=true')
        .respond([...]);
    $httpBackend.whenGET(baseApiUrl + 'majors?organization=RU').respond([...]);
});

Затем у меня есть первый шаг на моем сервере Jenkins CI, чтобы заменить studentsApp с studentsAppDev и добавить ссылку на angular-mocks.js в основном файле index.html.

Макетирование вашего бэкэнда - важный шаг в построении сложного приложения Angular. Это позволяет проводить тестирование без доступа к бэкэнду, вы не тестируете объекты дважды, и у вас меньше беспокойств.

Angular Multimocks - это простой способ проверить, как ваше приложение ведет себя с различными ответами от API.

Это позволяет вам определять наборы ложных ответов API для различных сценариев как файлы JSON.

Это также позволяет вам легко менять сценарии. Это достигается за счет того, что вы можете составлять "сценарии" из разных макетов файлов.

Как добавить его в ваше приложение

После добавления необходимых файлов на страницу просто добавьте scenario как зависимость от вашего приложения:

angular
  .module('yourAppNameHere', ['scenario'])
  // Your existing code here...

Как только вы добавите это в свое приложение, вы можете начать создавать макеты для вызовов API.

Допустим, ваше приложение выполняет следующий вызов API:

$http.get('/games').then(function (response) {
  $scope.games = response.data.games;
});

Вы можете создать default макет файла:

Пример someGames.json

{
  "httpMethod": "GET",
  "statusCode": 200,
  "uri": "/games",
  "response": {
    "games": [{"name": "Legend of Zelda"}]
  }
}

Когда вы загружаете свое приложение, звонки на /games вернусь 200 а также {"games": [{"name": "Legend of Zelda"}]}

Теперь предположим, что вы хотите вернуть другой ответ для того же вызова API, вы можете поместить приложение в другой сценарий, изменив URL, например ?scenario=no-games

no-games Сценарий может использовать другой макет файла, скажем так:

Пример noGames.json

{
  "httpMethod": "GET",
  "statusCode": 200,
  "uri": "/games",
  "response": {
    "games": []
  }
}

Теперь, когда вы загружаете свое приложение, звонки /games вернусь 200 а также {"games": []}

Сценарии состоят из различных макетов JSON в манифесте, подобном следующему:

{
  "_default": [
    "games/someGames.json"
  ],
  "no-games": [
    "games/noGames.json"
  ]
}

Затем вы можете исключить фиктивные файлы и удалить scenario зависимость в вашем производственном приложении.

Это больше похоже на юнит / спецификацию тестирования. Вообще говоря, вы должны использовать mocks в модульных / спецификационных тестах, а не в e2e / интеграционных тестах. По сути, думайте о тестах e2e как об утверждении ожиданий от в основном интегрированного приложения... имитация вещей как бы побеждает цель тестирования e2e. На самом деле, я не уверен, как karam вставит angular-mocks.js в работающее приложение.

Тест спецификации может выглядеть примерно так...

describe('Controller: MainCtrl', function () {
    'use strict';

    beforeEach(module('App.main-ctrl'));

    var MainCtrl,
        scope,
        $httpBackend;

    beforeEach(inject(function ($controller, $rootScope, $injector) {
        $httpBackend = $injector.get('$httpBackend');
        $httpBackend.when('GET', '/search/mow').respond([
            {}
        ]);
        scope = $rootScope.$new();
        MainCtrl = $controller('MainCtrl', {
            $scope: scope
        });
    }));

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

    it('should search for mow', function () {
        scope.search = 'mow';
        $httpBackend.flush();
        expect(scope.accounts.length).toBe(1);
    });
});
Другие вопросы по тегам