2016-08-02 104 views
1

我花了整整一个下午的时间尝试编写一个单元测试,以发现按预期工作但没有成功的服务。问题来自于注入的依赖关系,因为我有另一个通过单元测试的服务(它没有依赖关系)。Angular JS嵌套服务单元测试

这一服务

import angular from 'angular'; 
 

 
export class IPMAService { 
 
    /*@ngInject*/ 
 
    constructor($http, localStorageService, appConfig, locationStorageKey) { 
 
    debugger; 
 
    this.$http = $http; 
 
    this.localStorage = localStorageService; 
 
    this.appConfig = appConfig; 
 
    this.locationStorageKey = locationStorageKey; 
 
    if (Object.keys(this.localStorage.get(locationStorageKey)).length < 1) { 
 
     console.log("IPMAService - Initialization (locations not present in local storage)"); 
 
     this._getLocationsFromService(); 
 
    } 
 
    } 
 

 
    getLocations() { 
 
    console.log("IPMAService - Get Locations"); 
 
    var locations = this.localStorage.get(this.locationStorageKey); 
 
    if(!locations){ 
 
     return this._getLocationsFromService(); 
 
    } 
 
    return locations; 
 
    }; 
 

 
    _getLocationsFromService(){ 
 
     var _self = this; 
 
     this.$http({ 
 
     method: 'GET', 
 
     url: this.appConfig.ipmaWebServices.locations 
 
     }).then((response) => { 
 
     _self.localStorage.set(_self.locationStorageKey, response.data); 
 
     console.log("IPMAService - Saved locations.json to local storage with key: " + _self.locationStorageKey); 
 
     return response; 
 
     }) 
 
     .catch((response) => { 
 
     console.log("IPMAService - Erro no acesso à api do IPMA: " + _self.appConfig.ipmaWebServices.locations); 
 
     }) 
 
    } 
 
} 
 

 
export default angular.module('services.ipmaService', []) 
 
    .service('ipmaService', IPMAService) 
 
    .name;

我读了很多关于单元测试,我似乎无法得到它的工作。这是我最后一次尝试(甚至没有基本的.toBeDefined(),因为它在注入功能崩溃:“类型错误:无法转换未定义或为空反对”):

'use strict'; 
 

 
import serviceModule from './IPMAService.service'; 
 
import LocalStorageModule from 'angular-local-storage'; 
 
import constantsModule from '../../app/app.constants'; 
 

 
describe('Service: IPMAService', function() { 
 
    beforeEach(angular.mock.module(serviceModule)); 
 
    beforeEach(angular.mock.module(LocalStorageModule)); 
 
    beforeEach(angular.mock.module(constantsModule)); 
 

 
    var localStorageService2, service, httpBackend, ipmawebservices, locationStoreKey2; 
 

 
    beforeEach(inject(function (ipmaService, _$http_, localStorageService, appConfig, locationStoreKey) { 
 
     service = ipmaService; 
 
     httpBackend = _$http_; 
 
     localStorageService2 = localStorageService; 
 
     ipmawebservices = appConfig; 
 
     locationStoreKey2 = locationStorageKey; 
 

 
    })); 
 

 
    it ('should be loaded', function() { 
 
     expect(service).toBeDefined(); 
 
    }); 
 
});

另外...以后我得到这个基本的测试通过/工作,我想知道我怎么可以,例如,测试的getLocations方法,因为它取决于_getLocationsFromService方法和localStorage的(我想我不得不使用茉莉花的SpyOn,但有一点帮助窝很棒)。

在此先感谢。

编辑

这是我最后的溶液混合接受的答案和一些更多的研究。希望它能帮助那些对单元测试绝望的人。

:我删除了locationStorageKey依赖从服务

'use strict'; 
 

 
import serviceModule, { IPMAService } from './IPMAService.service'; 
 
import LocalStorageModule from 'angular-local-storage'; 
 
import constantsModule from '../../app/app.constants'; 
 

 
describe('Service: IPMAService', function() { 
 
    var mockDependency, appConfigDependency, service, 
 
    locationsObject = [{ local: "Lisboa", latitude: 33, longitude: 10 },{ local: "Porto", latitude: 36, longitude: 9 }]; 
 
    beforeEach(angular.mock.module(serviceModule)); 
 

 
    beforeEach(function() { 
 

 
     mockDependency = { 
 
      get: function() { 
 
       return locationsObject; 
 
      } 
 
     }; 
 

 
     appConfigDependency = { 
 

 
     }; 
 

 
     angular.mock.module(function ($provide) { 
 
      $provide.value('localStorageService', mockDependency); 
 
     }); 
 

 
     angular.mock.module(function ($provide) { 
 
      $provide.constant('appConfig', appConfigDependency); 
 
     }); 
 

 
    }); 
 

 
    beforeEach(() => { 
 
     spyOn(IPMAService.prototype, '_getLocationsFromService').and.returnValue(locationsObject); 
 
    }); 
 

 
    it('should return location object', inject(function (ipmaService) { 
 
     expect(ipmaService.getLocations()).toBe(locationsObject); 
 
    })); 
 

 

 
});

回答

1

的问题包含一个陷阱。不应该测试嵌套服务。单元测试是关于一次测试一个单元的。一项服务(ipmaService)已经过测试,其他服务则被嘲笑。

此列表

beforeEach(angular.mock.module(serviceModule)); 
beforeEach(angular.mock.module(LocalStorageModule)); 
beforeEach(angular.mock.module(constantsModule)); 

应该成为

var localStorageServiceMock = { get: jasmine.createSpy().and.returnValue(...) }; 

beforeEach(angular.mock.module(serviceModule, { 
    localStorageService: localStorageServiceMock, 
    appConfig: ..., 
    locationStorageKey: ... 
})); 

幸运的是,我们有ES6模块一个单独的类出口旁边有default

import serviceModule, { IPMAService } from './IPMAService.service'; 

这使我们有机会在服务即时之前在类原型方法上设置间谍iated与inject

beforeEach(() => { 
spyOn(IPMAService.prototype, '_getLocationsFromService').and.callThrough(); 
}); 
... 
expect(service._getLocationsFromService).toHaveBeenCalled(); 

或者,该方法可以在一些服务规格嘲笑改善粒度和集中在测试方法。

在构造函数中监听服务自己的方法并不是什么大不了的事情,但它增加了规范的实力。

+0

早上我会先试一试,感谢您的快速回复! – Bernardo

+0

我最终将自己的答案与我看到的其他答案混合在一起,然后用我的解决方案编辑了我的帖子。感谢您的帮助:) – Bernardo

+0

@Bernardo不客气。 – estus