简体   繁体   English

Angular - 为什么我的弹珠测试不适用于包含用户权限的 BehaviorSubject?

[英]Angular - why doesn't my marbles test work for my BehaviorSubject that contains user permissions?

I'm writing an app with Angular 8.我正在用 Angular 8 编写一个应用程序。

I decided to use an rxjs store with a BehaviorSubject for simple state management.我决定使用带有 BehaviorSubject 的 rxjs 商店来进行简单的 state 管理。

Basically, my code is a service that will store, load and update the user permissions.基本上,我的代码是一个存储、加载和更新用户权限的服务。 Sometimes this will come from the server after an HTTP call and sometimes this information will come from local storage.有时这将在 HTTP 调用后来自服务器,有时此信息将来自本地存储。 All subscribers will get updated after the user permissions have changed.用户权限更改后,所有订阅者都将得到更新。

Here is my StackBlitz: https://stackblitz.com/edit/rxjs-state-management-get-set Please click the 'add profile' button to see it in action.这是我的 StackBlitz: https://stackblitz.com/edit/rxjs-state-management-get-set请单击“添加配置文件”按钮以查看它的运行情况。

Here is my StackBlitz with the unit tests set up: https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr这是我设置了单元测试的StackBlitz:https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr

I have written a few unit tests in there using the basic subscriber and done technique but I want to dig a little deeper.我已经使用基本的订阅者和完成的技术在那里编写了一些单元测试,但我想更深入地挖掘。

I have read that we can use 'marbles' to test the value of an Observable over time: https://rxjs-dev.firebaseapp.com/guide/testing/internal-marble-tests我已经读到我们可以使用'marbles'来测试 Observable 的值随着时间的推移: https://rxjs-dev.firebaseapp.com/guide/testing/internal-marble-tests

I want to use marbles to test the 'load' function in my code.我想在我的代码中使用弹珠来测试“加载”function。

Code snippet of service store:服务商店的代码片段:

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

}

Basically the 'load' function checks to see if there is any data stored in the local storage and if there is, it will update the BehaviorSubject value.基本上,“加载” function 检查本地存储中是否存储了任何数据,如果有,它将更新 BehaviorSubject 值。 I want to test how the value changes over time eg it was value 1 before calling the load function and not it is value 2 after calling the function.我想测试值如何随时间变化,例如在调用负载 function 之前它是值 1,而不是在调用 function 之后它是值 2。

03/07/2020 Attempt at marbles test ---- Edit: 03/07/2020 尝试弹珠测试----编辑:

Here is my code snippet for my marbles test:这是我的弹珠测试的代码片段:

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

Error:错误:

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>

Example: https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr?embed=1&file=src/testing/persona.service.spec.ts示例: https://stackblitz.com/edit/rxjs-state-management-get-set-xpptmr?embed=1&file=src/testing/persona.service.spec.ts

What we nee to do is use ReplaySubject to capture all of the values that the Observable had.我们需要做的是使用 ReplaySubject 来捕获 Observable 拥有的所有值。 If we use BehaviourSubject, it will only give us the final value and not all of the values that it was in the past.如果我们使用 BehaviourSubject,它只会给我们最终的价值,而不是过去的所有价值。

example:例子:

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

}));

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

相关问题 Angular BehaviorSubject; “下一个”方法不起作用 - Angular BehaviorSubject; the “next” method doesn't work 为什么我的 AuthGuard 不起作用,BehaviorSubject - why my AuthGuard didn't works, BehaviorSubject 为什么我的 Angular 8 项目在 GitHub 页面上不起作用 - Why my Angular 8 project doesn't work on GitHub pages 为什么我的“onclick()”事件在 Angular 中不起作用? - Why my "onclick()" event doesn't work in Angular? 为什么我的路由器导航 function 在 angular 8 中不起作用? - Why my router navigate function doesn't work in angular 8? 为什么我在 Angular 中的 observable 不能按预期工作? - Why doesn't my observable in Angular work as expected? 我不知道为什么我的 http 请求在我的 angular 项目中不起作用? - I don't know why my http request doesn't work in my angular project? 为什么我的Angular测试不喜欢我的自定义表单验证功能? - Why doesn't my Angular test like my custom form validation function? localstorage 在我的 angular 应用程序中不起作用 - localstorage doesn't work in my angular application Angular 2 组件不适用于我的 javascript - Angular 2 component doesn't work with my javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM