Angular - 为什么我的弹珠测试对包含用户权限的 BehaviorSubject 不起作用?
Angular - why doesn't my marbles test work for my BehaviorSubject that contains user permissions?
我正在用 Angular 8.
编写一个应用程序
我决定使用带有 BehaviorSubject 的 rxjs 存储来进行简单的状态管理。
基本上,我的代码是一个将存储、加载和更新用户权限的服务。有时这将来自 HTTP 调用后的服务器,有时该信息将来自本地存储。用户权限更改后,所有订阅者都会得到更新。
这是我的 StackBlitz:https://stackblitz.com/edit/rxjs-state-management-get-set
请点击 'add profile' 按钮查看实际效果。
这是我的设置了单元测试的 StackBlitz:https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr
我已经使用基本订阅者和完成技术在其中编写了一些单元测试,但我想更深入地挖掘。
我读到我们可以使用 'marbles' 来测试 Observable 随时间变化的值:https://rxjs-dev.firebaseapp.com/guide/testing/internal-marble-tests
我想使用弹珠来测试我代码中的 'load' 函数。
服务商店的代码片段:
@Injectable({
providedIn: 'root'
})
export class PersonaService {
private readonly data: BehaviorSubject<IPersonaData>;
public readonly sharedData: Observable<IPersonaData>;
constructor() {
this.data = new BehaviorSubject<IPersonaData>(null);
this.sharedData = this.data.asObservable() as Observable<IPersonaData>;
}
public load(): void {
const storedPersonaData: IPersonaData = {
currentPersonaIndex: null,
personas: []
};
const storedCurrentIndex: string = localStorage.getItem(Constants.KEYS.defaultPersonaIndex) as string;
if (storedCurrentIndex != null) {
storedPersonaData.currentPersonaIndex = parseInt(storedCurrentIndex, Constants.RADIX.BASE_10);
}
const storedPersonas: string = localStorage.getItem(Constants.KEYS.personas) as string;
if (storedPersonas != null) {
storedPersonaData.personas = JSON.parse(storedPersonas) as IPersona[];
}
this.data.next(storedPersonaData);
}
public clear(): void {
this.data.next(null);
}
}
基本上,'load' 函数会检查本地存储中是否存储了任何数据,如果有,它将更新 BehaviorSubject 值。我想测试值如何随时间变化,例如在调用加载函数之前它是值 1 而不是在调用函数之后它是值 2。
03/07/2020 尝试弹珠测试----编辑:
这是我的弹珠测试代码片段:
fit("clear", () =>
testScheduler.run(({ expectObservable }) => {
const newPersonaData: IPersonaData = {
currentPersonaIndex: 1,
personas: [...mockPersonas] //TODO: is this the best way to make a copy of it???
};
//put the data in the behaviour subject first
localStorage.setItem(
Constants.KEYS.defaultPersonaIndex,
newPersonaData.currentPersonaIndex.toString()
);
localStorage.setItem(
Constants.KEYS.personas,
JSON.stringify(newPersonaData.personas)
);
//service.sharedData - initial value should be null
service.load(); //service.sharedData - value should be newPersonaData
service.clear(); //service.sharedData - value should be null
expectObservable(service.sharedData).toBe("abc", {
a: null,
b: newPersonaData,
c: null
});
}));
错误:
finished in 0.368sRan 1 of 9 specs - run all1 spec, 1 failure, randomized with seed 98448Spec List | FailuresSpec List | Failures
PersonaService > clear
Expected $.length = 1 to equal 3. Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: Object({ currentPersonaIndex: 1, personas: [ Object({ PersonaId: 154, Current: true, Description: 'Employee', SecuritySelectionId: 804356, DefaultSelectionId: 0, ProfileId: 17, ProfileName: '', IsSingleEmployee: true, DefaultEmpId: 714, EmpNo: '305', Level: -2, LevelDescription: 'Employee 1', AccessBand: 0, AuthBand: 0, PersonaIndex: 0, ExpiryDate: ' ', CoveringUserName: '', CoveringStartDate: ' ', CoveringEndDate: ' ', mobileaccess: true, canclock: true, mustsavelocation: true, canbookholiday: true, viewroster: true, canbookabsence: true, canviewbalancedetails: true, canviewactivity: true, canviewtimesheet: false, canviewtimesheetapproval: false, View_Shift_Swap: false, View_Flexi: true, View_Team_Calendar: false, Cancel_Absence: true, Delete_Absence: false, Mobile_Upload_Photo: true, Mobile_Upload_Absence_Photo: true, Receive_Notifications: false, CanViewRosterV2: false, AbsenceActions: [ Object({ .... Expected $[2] = undefined to equal Object({ frame: 2, notification: Notification({ kind: 'N', value: null, error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 3. Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: Object({ currentPersonaIndex: 1, personas: [ Object({ PersonaId: 154, Current: true, Description: 'Employee', SecuritySelectionId: 804356, DefaultSelectionId: 0, ProfileId: 17, ProfileName: '', IsSingleEmployee: true, DefaultEmpId: 714, EmpNo: '305', Level: -2, LevelDescription: 'Employee 1', AccessBand: 0, AuthBand: 0, PersonaIndex: 0, ExpiryDate: ' ', CoveringUserName: '', CoveringStartDate: ' ', CoveringEndDate: ' ', mobileaccess: true, canclock: true, mustsavelocation: true, canbookholiday: true, viewroster: true, canbookabsence: true, canviewbalancedetails: true, canviewactivity: true, canviewtimesheet: false, canviewtimesheetapproval: false, View_Shift_Swap: false, View_Flexi: true, View_Team_Calendar: false, Cancel_Absence: true, Delete_Absence: false, Mobile_Upload_Photo: true, Mobile_Upload_Absence_Photo: true, Receive_Notifications: false, CanViewRosterV2: false, AbsenceActions: [ Object({ .... Expected $[2] = undefined to equal Object({ frame: 2, notification: Notification({ kind: 'N', value: null, error: undefined, hasValue: true }) }). at <Jasmine> at TestScheduler.deepEqual [as assertDeepEqual] (https://rxjs-state-management-get-set-xpptmr.stackblitz.io/~/src/testing/persona.service.spec.ts:8:20) at eval (https://rxjs-state-management-get-set-xpptmr.stackblitz.io/turbo_modules/rxjs@6.5.5/internal/testing/TestScheduler.js:133:23) at <Jasmine>
我们需要做的是使用 ReplaySubject 来捕获 Observable 拥有的所有值。
如果我们使用 BehaviourSubject,它只会给我们最终值,而不是过去的所有值。
示例:
it('2a clear - should clear the data from the BehaviorSubject - should be null, data, null', () => testScheduler.run(({ expectObservable }) => {
const replaySubject$ = new ReplaySubject<IPersonaData>();
service.sharedData.subscribe(replaySubject$);
const newPersonaData: IPersonaData = {
currentPersonaIndex: 1,
personas: [...mockPersonas] //TODO: is this the best way to make a copy of it???
};
//put the data in the behaviour subject first
appSettings.setString(Constants.KEYS.defaultPersonaIndex, newPersonaData.currentPersonaIndex.toString());
appSettings.setString(Constants.KEYS.personas, JSON.stringify(newPersonaData.personas));
service.load();
service.clear();
expectObservable(replaySubject$).toBe('(abc)', { a: null, b: newPersonaData, c: null });
}));
我正在用 Angular 8.
编写一个应用程序我决定使用带有 BehaviorSubject 的 rxjs 存储来进行简单的状态管理。
基本上,我的代码是一个将存储、加载和更新用户权限的服务。有时这将来自 HTTP 调用后的服务器,有时该信息将来自本地存储。用户权限更改后,所有订阅者都会得到更新。
这是我的 StackBlitz:https://stackblitz.com/edit/rxjs-state-management-get-set 请点击 'add profile' 按钮查看实际效果。
这是我的设置了单元测试的 StackBlitz:https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr
我已经使用基本订阅者和完成技术在其中编写了一些单元测试,但我想更深入地挖掘。
我读到我们可以使用 'marbles' 来测试 Observable 随时间变化的值:https://rxjs-dev.firebaseapp.com/guide/testing/internal-marble-tests
我想使用弹珠来测试我代码中的 'load' 函数。
服务商店的代码片段:
@Injectable({
providedIn: 'root'
})
export class PersonaService {
private readonly data: BehaviorSubject<IPersonaData>;
public readonly sharedData: Observable<IPersonaData>;
constructor() {
this.data = new BehaviorSubject<IPersonaData>(null);
this.sharedData = this.data.asObservable() as Observable<IPersonaData>;
}
public load(): void {
const storedPersonaData: IPersonaData = {
currentPersonaIndex: null,
personas: []
};
const storedCurrentIndex: string = localStorage.getItem(Constants.KEYS.defaultPersonaIndex) as string;
if (storedCurrentIndex != null) {
storedPersonaData.currentPersonaIndex = parseInt(storedCurrentIndex, Constants.RADIX.BASE_10);
}
const storedPersonas: string = localStorage.getItem(Constants.KEYS.personas) as string;
if (storedPersonas != null) {
storedPersonaData.personas = JSON.parse(storedPersonas) as IPersona[];
}
this.data.next(storedPersonaData);
}
public clear(): void {
this.data.next(null);
}
}
基本上,'load' 函数会检查本地存储中是否存储了任何数据,如果有,它将更新 BehaviorSubject 值。我想测试值如何随时间变化,例如在调用加载函数之前它是值 1 而不是在调用函数之后它是值 2。
03/07/2020 尝试弹珠测试----编辑:
这是我的弹珠测试代码片段:
fit("clear", () =>
testScheduler.run(({ expectObservable }) => {
const newPersonaData: IPersonaData = {
currentPersonaIndex: 1,
personas: [...mockPersonas] //TODO: is this the best way to make a copy of it???
};
//put the data in the behaviour subject first
localStorage.setItem(
Constants.KEYS.defaultPersonaIndex,
newPersonaData.currentPersonaIndex.toString()
);
localStorage.setItem(
Constants.KEYS.personas,
JSON.stringify(newPersonaData.personas)
);
//service.sharedData - initial value should be null
service.load(); //service.sharedData - value should be newPersonaData
service.clear(); //service.sharedData - value should be null
expectObservable(service.sharedData).toBe("abc", {
a: null,
b: newPersonaData,
c: null
});
}));
错误:
finished in 0.368sRan 1 of 9 specs - run all1 spec, 1 failure, randomized with seed 98448Spec List | FailuresSpec List | Failures
PersonaService > clear
Expected $.length = 1 to equal 3. Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: Object({ currentPersonaIndex: 1, personas: [ Object({ PersonaId: 154, Current: true, Description: 'Employee', SecuritySelectionId: 804356, DefaultSelectionId: 0, ProfileId: 17, ProfileName: '', IsSingleEmployee: true, DefaultEmpId: 714, EmpNo: '305', Level: -2, LevelDescription: 'Employee 1', AccessBand: 0, AuthBand: 0, PersonaIndex: 0, ExpiryDate: ' ', CoveringUserName: '', CoveringStartDate: ' ', CoveringEndDate: ' ', mobileaccess: true, canclock: true, mustsavelocation: true, canbookholiday: true, viewroster: true, canbookabsence: true, canviewbalancedetails: true, canviewactivity: true, canviewtimesheet: false, canviewtimesheetapproval: false, View_Shift_Swap: false, View_Flexi: true, View_Team_Calendar: false, Cancel_Absence: true, Delete_Absence: false, Mobile_Upload_Photo: true, Mobile_Upload_Absence_Photo: true, Receive_Notifications: false, CanViewRosterV2: false, AbsenceActions: [ Object({ .... Expected $[2] = undefined to equal Object({ frame: 2, notification: Notification({ kind: 'N', value: null, error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 3. Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: Object({ currentPersonaIndex: 1, personas: [ Object({ PersonaId: 154, Current: true, Description: 'Employee', SecuritySelectionId: 804356, DefaultSelectionId: 0, ProfileId: 17, ProfileName: '', IsSingleEmployee: true, DefaultEmpId: 714, EmpNo: '305', Level: -2, LevelDescription: 'Employee 1', AccessBand: 0, AuthBand: 0, PersonaIndex: 0, ExpiryDate: ' ', CoveringUserName: '', CoveringStartDate: ' ', CoveringEndDate: ' ', mobileaccess: true, canclock: true, mustsavelocation: true, canbookholiday: true, viewroster: true, canbookabsence: true, canviewbalancedetails: true, canviewactivity: true, canviewtimesheet: false, canviewtimesheetapproval: false, View_Shift_Swap: false, View_Flexi: true, View_Team_Calendar: false, Cancel_Absence: true, Delete_Absence: false, Mobile_Upload_Photo: true, Mobile_Upload_Absence_Photo: true, Receive_Notifications: false, CanViewRosterV2: false, AbsenceActions: [ Object({ .... Expected $[2] = undefined to equal Object({ frame: 2, notification: Notification({ kind: 'N', value: null, error: undefined, hasValue: true }) }). at <Jasmine> at TestScheduler.deepEqual [as assertDeepEqual] (https://rxjs-state-management-get-set-xpptmr.stackblitz.io/~/src/testing/persona.service.spec.ts:8:20) at eval (https://rxjs-state-management-get-set-xpptmr.stackblitz.io/turbo_modules/rxjs@6.5.5/internal/testing/TestScheduler.js:133:23) at <Jasmine>
我们需要做的是使用 ReplaySubject 来捕获 Observable 拥有的所有值。 如果我们使用 BehaviourSubject,它只会给我们最终值,而不是过去的所有值。
示例:
it('2a clear - should clear the data from the BehaviorSubject - should be null, data, null', () => testScheduler.run(({ expectObservable }) => {
const replaySubject$ = new ReplaySubject<IPersonaData>();
service.sharedData.subscribe(replaySubject$);
const newPersonaData: IPersonaData = {
currentPersonaIndex: 1,
personas: [...mockPersonas] //TODO: is this the best way to make a copy of it???
};
//put the data in the behaviour subject first
appSettings.setString(Constants.KEYS.defaultPersonaIndex, newPersonaData.currentPersonaIndex.toString());
appSettings.setString(Constants.KEYS.personas, JSON.stringify(newPersonaData.personas));
service.load();
service.clear();
expectObservable(replaySubject$).toBe('(abc)', { a: null, b: newPersonaData, c: null });
}));