Служба тестирования, которая возвращает обещание в Angular

#angularjs #testing #typescript #karma-jasmine

#angularjs #тестирование #машинописный текст #карма-жасмин #typescript

Вопрос:

Я пытаюсь протестировать свои сервисы и данные, которые они возвращают. Я использую Angular, Typescript и Karma. Это мой сервис

 /// <reference path="../../../typings/index.d.ts" />

class WeatherDataService {

  static $inject = ['$http', '$log', '$q'];

  constructor(private $http: ng.IHttpService,
              private $log: ng.ILogService,
              private $q: ng.IQService) {
    this.$http = $http;
    this.$log = $log;
    this.$q = $q;
  }

  get(params: string): ng.IPromise<{}> {
    var self = this;
    var deferred = this.$q.defer();

    this.$http.get('http://api.openweathermap.org/data/2.5/weather?'   params   'amp;appid='   API_KEY)
      .then((response) => {
        deferred.resolve(response.data);
      }, (errors) => {
        self.$log.debug(errors);
        deferred.reject(errors.data);
      });

    return deferred.promise;
  }
}
  

И это WeatherDataService.spec.ts

 /// <reference path="../../../typings/index.d.ts" />

describe('WeatherDataService', () => {
  let weatherDataService;

  var $http, $log, $q;

  beforeEach(angular.mock.module('app'));

  beforeEach(inject(function (_$http_: ng.IHttpService, _$log_: ng.ILogService, _$q_: ng.IQService) {
    $http = _$http_;
    $log = _$log_;
    $q = _$q_;

    weatherDataService = new WeatherDataService($http, $log, $q);
  }));

  describe('testing karma', function () {
    it('Geolocation response should have a valid structure', function () {
      weatherDataService.get('lat=42.683529199999995amp;lon=26.309871599999997').then((response) => {
        expect(response).toBe('object');
      });
    });
  });
});
  

Мой главный вопрос заключается в том, как протестировать deferred.promise и проверить структуру полученных данных.

______________________ПРАВИТЬ______________________

Это то, что я пробую в спецификации

 /// <reference path="../../../typings/index.d.ts" />

describe('WeatherDataServices', () => {

  let weatherDataService;

  beforeEach(angular.mock.module('app'));

  beforeEach(angular.mock.module('weatherDataService'));

  var service, $httpBackend, $http,
    $scope = {valid: true, response: {}};

  beforeEach(inject(function ($injector: ng.auto.IInjectorService) {
    service = $injector.get('WeatherDataService');
    $httpBackend = $injector.get('$httpBackend');
    $http = $injector.get('$http');
  }));

  it('should demonstrate using when (200 status)', () => {
    $http.get('http://jsonplaceholder.typicode.com/users')
      .success(function(data: any) {
        $scope.valid = true;
        $scope.response = data;
      })
      .error(function(data: any) {
        $scope.valid = false;
      });

    expect($scope.valid).toBe(true);
  });
});
  

Я получаю TypeError: undefined is not an object (evaluating '$http.get') . Я просто хочу протестировать свой сервис, который есть weatherDataService.get(params) , и он работает в моем контроллере.

______________________ПРАВИТЬ______________________

Это ошибка, которую я получаю с предоставленным ответом.

 Geolocation response should have a valid structure FAILED
forEach@bower_components/angular/angular.js:321:24
loadModules@bower_components/angular/angular.js:4601:12
createInjector@bower_components/angular/angular.js:4523:30
workFn@bower_components/angular-mocks/angular-mocks.js:3074:60
loaded@http://localhost:9876/context.js:151:17
bower_components/angular/angular.js:4641:53
forEach@bower_components/angular/angular.js:321:24
loadModules@bower_components/angular/angular.js:4601:12
createInjector@bower_components/angular/angular.js:4523:30
workFn@bower_components/angular-mocks/angular-mocks.js:3074:60
loaded@http://localhost:9876/context.js:151:17
bower_components/angular/angular.js:4641:53
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.006 secs / 0.014 secs)
[15:24:52] 'karma:single-run' errored after 2.26 s
[15:24:52] Error: Failed 1 tests.
at failCount (F:varwwwcompucorptask1gulp_taskskarma.js:13:22)
at removeAllListeners
(F:varwwwcompucorptask1node_moduleskarmalibserver.js:379:7)
at Server.<anonymous>
(F:varwwwcompucorptask1node_moduleskarmalibserver.js:390:9)
at Server.g (events.js:286:16)
at emitNone (events.js:91:20)
at Server.emit (events.js:185:7)
at emitCloseNT (net.js:1548:8)
at _combinedTickCallback (internal/process/next_tick.js:71:11)
at process._tickDomainCallback (internal/process/next_tick.js:122:9)
[15:24:52] 'test' errored after 3.87 s
npm ERR! Test failed.  See above for more details.
  

Комментарии:

1. Способ запуска expect — запустить дайджест с помощью $rootScope. $apply() после этого. github.com/bvaughn/jasmine-promise-matchers это хорошая альтернатива. Кстати, в коде используется отложенный антипаттерн .

2. Я ввел rootscope и попробовал $rootScope.$apply(); после этого, но это все то же самое. Можете ли вы опубликовать решение в качестве ответа?

3. Я добавил это в качестве ответа. Я не вижу, где высмеивается http-запрос $. Если реальная спецификация выглядит так же, она не будет работать по этой причине.

4. Можете ли вы добавить код в свой ответ, потому что я пытался, и я получаю undefined при попытке ввести http?

5. Пожалуйста, обновите код в вопросе в соответствии с вашей текущей спецификацией, чтобы было ясно, что такое «неопределенный».

Ответ №1:

Способ запуска expect — запустить дайджест с $rootScope.$apply() или $rootScope.$digest() после.

Также более выгодно просто вводить weatherDataService вместо создания экземпляра вручную new WeatherDataService($http, $log, $q) . Это может быть делом вкуса, но проблема с ручным подходом заключается в том, что он не проверяет аннотацию сервиса. Так что пусть Angular сделает эту работу за вас.

   let $httpBackend, $rootScope;

  beforeEach(inject(function (_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService) {
    $httpBackend = _$httpBackend_;
    $rootScope = _$rootScope_;
  });

it('Geolocation response should have a valid structure', inject(function (weatherData) {
  const params = 'lat=42.683529199999995amp;lon=26.309871599999997';
  const API_KEY = ...;
  const url = 'http://api.openweathermap.org/data/2.5/weather?'   params   'amp;appid='   API_KEY;

  const responseMock = {};

  $httpBackend.whenGET(url).respond(responseMock );
  weatherData.get(params).then((response) => {
    expect(response).toBe(responseMock);
  });

  // does $rootScope.$digest() internally
  $httpBackend.flush();
  $httpBackend.verifyNoOutstandingRequest();
}));
  

jasmine-promise-matchers предоставляет хорошую альтернативу утверждениям, завернутым в обещания (по сути, они выполняют то же самое, что и приведенный выше код).

Комментарии:

1. Теперь я получаю WeatherDataService testing karma Ответ на геолокацию должен иметь допустимую структуру Ошибка ОШИБКИ: неожиданный запрос: ПОЛУЧИТЬ api.openweathermap.org/data/2.5 /…

2. Видишь docs.angularjs.org/api/ngMock/service /$httpBackend о том, как следует тестировать $http. Что касается обещаний, то все очень просто: $rootScope. $digest() должен запускаться после каждой цепочки обещаний.

3. Можете ли вы обновить свой ответ целым кодом, который, по вашему мнению, будет тестировать WeatherDataService и возвращенное обещание.

4. Это должно выглядеть так. Я не могу гарантировать, что код будет работать для вас, потому что ваш случай сложный и может отличаться от кода, который был показан.

5. Если вопрос выявляет дополнительные проблемы, которые не были очевидны на момент написания, хорошей практикой является создание новых вопросов, раскрывающих каждую из проблем, вместо добавления дополнительных вопросов к тому же вопросу. Таким образом, на вопросы можно получить быстрые и качественные ответы.