Angular Jest或Jasmine测试:如何正确地监视/模拟从测试类中调用的静态对象?
我有一个AppConfigService,它将一个对象从JSON文件加载到作为服务一部分的静态设置变量中。整个应用程序中的各种组件和/或服务使用AppConfigService.settings.引用对象,并使用简单引用(无注入)。如何测试引用这种构造的服务 比如说Angular Jest或Jasmine测试:如何正确地监视/模拟从测试类中调用的静态对象?,angular,testing,jasmine,jestjs,angular8,Angular,Testing,Jasmine,Jestjs,Angular8,我有一个AppConfigService,它将一个对象从JSON文件加载到作为服务一部分的静态设置变量中。整个应用程序中的各种组件和/或服务使用AppConfigService.settings.引用对象,并使用简单引用(无注入)。如何测试引用这种构造的服务 比如说 @Injectable() export class SomeService { someVariable; constructor() { // I can't get the test to not give me
@Injectable()
export class SomeService {
someVariable;
constructor() {
// I can't get the test to not give me a TypeError: Cannot read property 'someSettingsVariable' of undefined on this line
this.someVariable = AppConfigService.settings.someSettingsVariable;
}
}
我有两个项目,一个是使用Jest,另一个是Jasmine/Karma,我需要弄清楚如何让测试在这个结构中工作
我尝试过以下方法:
const spy = spyOnProperty(SomeService, 'someVariable')
.and.returnValue('someValue');
规范示例:
import { TestBed } from '@angular/core/testing';
import { NgRedux } from '@angular-redux/store';
import { Injectable } from '@angular/core';
import { DispatchHelper } from '../reducers/dispatch.helper';
import { ContributorActions } from '../actions/contributor.action';
import { MockDispatchHelper } from '../_mocks/DispatchHelperMock';
import { DiscrepancyService } from '../discrepancies/discrepancy.service';
import { DiscrepancyAPIService } from '../discrepancies/discrepancy-api.service';
import { DiscrepancyAPIServiceMock } from '../_mocks/DiscrepancyAPIServiceMock';
import { Observable } from 'rxjs';
import { Guid } from 'guid-typescript';
import { getInitialUserAccountState } from '../functions/initial-states/user-account-initial-state.function';
import { LoggingService } from '../security/logging/logging.service';
import { MockLoggingService } from '../_mocks/LoggingServiceMock';
describe('discrepancyService', () => {
let discrepancyService: DiscrepancyService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: Injectable, useClass: Injectable },
{ provide: DispatchHelper, useClass: MockDispatchHelper },
{ provide: ContributorActions, useClass: ContributorActions },
{ provide: NgRedux, useClass: NgRedux },
{ provide: DiscrepancyService, useClass: DiscrepancyService },
{ provide: DiscrepancyAPIService, useClass: DiscrepancyAPIServiceMock },
{ provide: LoggingService, useClass: MockLoggingService },
]
})
.compileComponents();
const userStateObservable = Observable.create(observer => {
const userState = getInitialUserAccountState();
userState.userId = Guid.parse('<guid>');
userState.organization_id = Guid.parse('<guid>');
observer.next(userState);
console.log('built user state observable');
observer.complete();
});
discrepancyService = TestBed.get(DiscrepancyService);
const spy4 = spyOnProperty(discrepancyService, 'userState$', 'get').and.returnValue(userStateObservable);
});
// TODO: Fix this
it('should create service and loadDiscrepancies', () => {
// in this example, discrepancyService constructor sets the
// value of a variable = ApiConfigService.settings.endPoint
// ApiConfigService.settings is static; how do I "replace"
// the value of endPoint in a call like this so I don't get
// an error because ApiConfigService.settings is undefined
// when called from a service in the test?
const spy = spyOn(discrepancyService.dispatcher, 'dispatchPayload');
discrepancyService.loadDiscrepancies();
expect(spy.calls.count()).toEqual(1);
});
});
任何来自测试大师的帮助都将不胜感激。我可以从三个方面了解它 直接设置值 添加一个空检查和一个setter 将静态实现隐藏在服务后面 这对我来说是迄今为止最好的解决办法。与其使用静态实现,不如创建一个易于监视的单实例服务。这不仅是您可以想象的问题,也是所有避免静态实现的OOP语言的问题
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ConfigService {
private endpoint: string;
constructor() { }
get endPoint(): string {
return this.endPoint;
}
}
运行时角度配置的完整示例请多分享一点您的规范文件,如果您还包括测试台的配置,您可能会获得有关问题的帮助Hi MapLion,请添加更多信息。您尝试的方法应该有效,但某个变量真的是属性还是仅仅是变量成员?如果它是一个静态变量,只需更改该值,而不是监视它。那里没有什么可监视的。对不起,我会这么做的。从技术上讲,这不是我的项目,但我会尝试获取更多信息。我想我添加了相关的部分。@AthanasiosKataras我如何“改变价值”,也许这就是我缺少的。我通常不知道如何在另一个正在测试的函数中调用某个函数。让我知道这三个函数中的任何一个是否适合您。因此,您建议将其设置为单例并完全取消静态实现——这就是您所说的第三个选项的意思吗?直接设置该值对我来说不起作用,我也不想为所有内容添加空检查和设置程序。创建配置服务。我已经添加了一些示例代码和完整解决方案的参考。因此,经过大量的实验和反复试验(您的建议都不起作用),我发现我的根本问题是本线程中描述的竞争条件:注意,这还包括您提供的链接,它似乎应该起作用,但在我的情况下不是这样的。我按照线程中的最后一个示例进行了修改,也遵循了这一点:最终,我去掉了静态变量,这导致我的测试再次工作。我将把这个标记为已接受。
// TODO: Fix this
it('should create service and loadDiscrepancies', () => {
// in this example, discrepancyService constructor sets the
// value of a variable = ApiConfigService.settings.endPoint
// ApiConfigService.settings is static; how do I "replace"
// the value of endPoint in a call like this so I don't get
// an error because ApiConfigService.settings is undefined
// when called from a service in the test?
AppConfigService.settings = { endpoint: 'http://endpoint' }
const spy = spyOn(discrepancyService.dispatcher, 'dispatchPayload');
discrepancyService.loadDiscrepancies();
expect(spy.calls.count()).toEqual(1);
});
@Injectable()
export class SomeService {
someVariable;
constructor() {
// I can't get the test to not give me a TypeError: Cannot read property 'someSettingsVariable' of undefined on this line
if (AppConfigService && AppConfigService.settings) {
this.someVariable = AppConfigService.settings.someSettingsVariable;
}
}
}
set endPoint(value) {
this.someVariable = value
}
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ConfigService {
private endpoint: string;
constructor() { }
get endPoint(): string {
return this.endPoint;
}
}