简体   繁体   English

在Angular中返回承诺的测试服务

[英]Testing service that returns promise in Angular

I'm trying to test my services and the data they return. 我正在尝试测试我的服务及其返回的数据。 I'm using Angular, Typescript and Karma. 我正在使用Angular,Typescript和Karma。 This is my service 这是我的服务

/// <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 + '&appid=' + API_KEY)
      .then((response) => {
        deferred.resolve(response.data);
      }, (errors) => {
        self.$log.debug(errors);
        deferred.reject(errors.data);
      });

    return deferred.promise;
  }
}

And this is the WeatherDataService.spec.ts 这是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.683529199999995&lon=26.309871599999997').then((response) => {
        expect(response).toBe('object');
      });
    });
  });
});

My main question is how to test the deferred.promise and check the structure of the received data. 我的主要问题是如何测试deferred.promise并检查接收到的数据的结构。

______________________EDIT______________________ ______________________编辑______________________

This is what I try in the spec 这就是我在规格中尝试的

/// <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);
  });
});

I get TypeError: undefined is not an object (evaluating '$http.get') . 我收到TypeError: undefined is not an object (evaluating '$http.get') I just want to test my service that is weatherDataService.get(params) and it works in my controller. 我只想测试我的服务weatherDataService.get(params) ,它可以在我的控制器中工作。

______________________EDIT______________________ ______________________编辑______________________

This is the error that I'm getting with the provided answer. 这是我提供的答案出现的错误。

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:\var\www\compucorp\task1\gulp_tasks\karma.js:13:22)
at removeAllListeners
(F:\var\www\compucorp\task1\node_modules\karma\lib\server.js:379:7)
at Server.<anonymous>
(F:\var\www\compucorp\task1\node_modules\karma\lib\server.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.

The way to run expect is to trigger the digest with $rootScope.$apply() or $rootScope.$digest() afterwards. 运行expect的方法是随后使用$rootScope.$apply()$rootScope.$digest()触发摘要。

It is also more beneficial to just inject weatherDataService instead of manual instantiation with new WeatherDataService($http, $log, $q) . 只注入weatherDataService而不是使用new WeatherDataService($http, $log, $q)手动实例化也更有利。 It could be a matter of taste, but the problem with manual approach is that it doesn't test service annotation. 可能是个问题,但是手动方法的问题是它不测试服务注释。 So let Angular do this job for you. 因此,让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.683529199999995&lon=26.309871599999997';
  const API_KEY = ...;
  const url = 'http://api.openweathermap.org/data/2.5/weather?' + params + '&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 provide a nice alternative to assertions wrapped in promises (they essentially do a thing similar to the code above). jasmine-promise-matchers提供了一个很好的替代方法,可以替代包装在Promise中的断言(它们本质上与上述代码类似)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM