在业力/茉莉花测试中模拟角度服务/承诺

时间:2022-08-24 22:30:02

I'm trying to write a karma/jasmine test and I would like some explanations about how mocks are working on a service which is returning a promise. I explain my situation :

我正在尝试编写业力/茉莉花测试,我想了解一些关于模拟如何处理正在返回承诺的服务的解释。我解释一下我的情况:

I have a controller in which I do the following call :

我有一个控制器,我在其中进行以下调用:

mapService.getMapByUuid(mapUUID, isEditor).then(function(datas){
    fillMapDatas(datas);
});

function fillMapDatas(datas){
    if($scope.elements === undefined){
        $scope.elements = [];
    }
 //Here while debugging my unit test, 'datas' contain the promise javascript object instead //of my real reponse.
   debugger;
    var allOfThem = _.union($scope.elements, datas.elements);

    ...

Here is how my service is :

以下是我的服务方式:

(function () {
'use strict';

var serviceId = 'mapService';

angular.module('onmap.map-module.services').factory(serviceId, [
    '$resource',
    'appContext',
    'restHello',
    'restMap',
    serviceFunc]);

function serviceFunc($resource, appContext, restHello, restMap) {

    var Maps = $resource(appContext+restMap, {uuid: '@uuid', editor: '@editor'});

    return{          
        getMapByUuid: function (uuid, modeEditor) {
            var maps = Maps.get({'uuid' : uuid, 'editor': modeEditor});
            return maps.$promise;
        }
    };
}

})();

And finally, here is my unit test :

最后,这是我的单元测试:

describe('Map controller', function() {
var $scope, $rootScope, $httpBackend, $timeout, createController, MapService, $resource;

beforeEach(module('onmapApp'));

beforeEach(inject(function($injector) {
    $httpBackend = $injector.get('$httpBackend');
    $rootScope = $injector.get('$rootScope');
    $scope = $rootScope.$new();

    var $controller = $injector.get('$controller');

    createController = function() {
        return $controller('maps.ctrl', {
            '$scope': $scope
        });
    };
}));

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

var response = {"elements":[1,2,3]};

it('should allow user to get a map', function() {

    var controller = createController();
    $httpBackend.expect('GET', '/onmap/rest/map/MY-UUID?editor=true')
        .respond({
            "success": response
        });


// hope to call /onmap/rest/map/MY-UUID?editor=true url and hope to have response as the fillMapDatas parameter
    $scope.getMapByUUID('MY-UUID', true); 

    $httpBackend.flush();
});
});

What I really want to do is to have my response object ( {"elements:...}) as the datas parameter of the fillMapDatas function. I don't understand how to mock all the service things (service, promise, then)

我真正想做的是将我的响应对象({“elements:...})作为fillMapDatas函数的datas参数。我不明白如何模拟所有服务事物(服务,承诺,然后)

1 个解决方案

#1


24  

So you want to test, if your service responses as expected? Then, this is something you would rather test on the service. Unit test promise based methods could look like this:

所以,如果您的服务响应符合预期,您想要测试吗?然后,这是你宁愿测试服务的东西。基于单元测试承诺的方法可能如下所示:

var mapService, $httpBackend, $q, $rootScope;

beforeEach(inject(function (_mapService_, _$httpBackend_, _$q_, _$rootScope_) {
  mapService = mapService;
  $httpBackend = _$httpBackend_;
  $q = _$q_;
  $rootScope = _$rootScope_;

  // expect the actual request
  $httpBackend.expect('GET', '/onmap/rest/map/uuid?editor=true');

  // react on that request
  $httpBackend.whenGET('/onmap/rest/map/uuid?editor=true').respond({
    success: {
      elements: [1, 2, 3]
    }
  });
}));

As you can see, you don't need to use $injector, since you can inject your needed services directly. If you wanna use the correct service names throughout your tests, you can inject them with prefixed and suffixed "_", inject() is smart enough to recognise which service you mean. We also setup the $httpBackend mock for each it() spec. And we set up $q and $rootScope for later processing.

如您所见,您不需要使用$ injector,因为您可以直接注入所需的服务。如果您想在整个测试过程中使用正确的服务名称,可以使用前缀和后缀“_”注入它们,inject()足够聪明,可以识别您的意思。我们还为每个it()规范设置了$ httpBackend模拟。我们设置了$ q和$ rootScope以供以后处理。

Here's how you could test that your service method returns a promise:

以下是测试服务方法返回承诺的方法:

it('should return a promise', function () {
  expect(mapService.getMapUuid('uuid', true).then).toBeDefined();
});

Since a promise always has a .then() method, we can check for this property to see if it's a promise or not (of course, other objects could have this method too).

由于promise始终具有.then()方法,因此我们可以检查此属性以查看它是否是一个promise(当然,其他对象也可以使用此方法)。

Next you can test of the promise you get resolves with the proper value. You can do that setting up a deferred that you explicitly resolve.

接下来,您可以测试您使用适当值解析的承诺。您可以设置您明确解决的延迟。

it('should resolve with [something]', function () {
  var data;

  // set up a deferred
  var deferred = $q.defer();
  // get promise reference
  var promise = deferred.promise;

  // set up promise resolve callback
  promise.then(function (response) {
    data = response.success;
  });

  mapService.getMapUuid('uuid', true).then(function(response) {
    // resolve our deferred with the response when it returns
    deferred.resolve(response);
  });

  // force `$digest` to resolve/reject deferreds
  $rootScope.$digest();

  // make your actual test
  expect(data).toEqual([something]);
});

Hope this helps!

希望这可以帮助!

#1


24  

So you want to test, if your service responses as expected? Then, this is something you would rather test on the service. Unit test promise based methods could look like this:

所以,如果您的服务响应符合预期,您想要测试吗?然后,这是你宁愿测试服务的东西。基于单元测试承诺的方法可能如下所示:

var mapService, $httpBackend, $q, $rootScope;

beforeEach(inject(function (_mapService_, _$httpBackend_, _$q_, _$rootScope_) {
  mapService = mapService;
  $httpBackend = _$httpBackend_;
  $q = _$q_;
  $rootScope = _$rootScope_;

  // expect the actual request
  $httpBackend.expect('GET', '/onmap/rest/map/uuid?editor=true');

  // react on that request
  $httpBackend.whenGET('/onmap/rest/map/uuid?editor=true').respond({
    success: {
      elements: [1, 2, 3]
    }
  });
}));

As you can see, you don't need to use $injector, since you can inject your needed services directly. If you wanna use the correct service names throughout your tests, you can inject them with prefixed and suffixed "_", inject() is smart enough to recognise which service you mean. We also setup the $httpBackend mock for each it() spec. And we set up $q and $rootScope for later processing.

如您所见,您不需要使用$ injector,因为您可以直接注入所需的服务。如果您想在整个测试过程中使用正确的服务名称,可以使用前缀和后缀“_”注入它们,inject()足够聪明,可以识别您的意思。我们还为每个it()规范设置了$ httpBackend模拟。我们设置了$ q和$ rootScope以供以后处理。

Here's how you could test that your service method returns a promise:

以下是测试服务方法返回承诺的方法:

it('should return a promise', function () {
  expect(mapService.getMapUuid('uuid', true).then).toBeDefined();
});

Since a promise always has a .then() method, we can check for this property to see if it's a promise or not (of course, other objects could have this method too).

由于promise始终具有.then()方法,因此我们可以检查此属性以查看它是否是一个promise(当然,其他对象也可以使用此方法)。

Next you can test of the promise you get resolves with the proper value. You can do that setting up a deferred that you explicitly resolve.

接下来,您可以测试您使用适当值解析的承诺。您可以设置您明确解决的延迟。

it('should resolve with [something]', function () {
  var data;

  // set up a deferred
  var deferred = $q.defer();
  // get promise reference
  var promise = deferred.promise;

  // set up promise resolve callback
  promise.then(function (response) {
    data = response.success;
  });

  mapService.getMapUuid('uuid', true).then(function(response) {
    // resolve our deferred with the response when it returns
    deferred.resolve(response);
  });

  // force `$digest` to resolve/reject deferreds
  $rootScope.$digest();

  // make your actual test
  expect(data).toEqual([something]);
});

Hope this helps!

希望这可以帮助!