代码之家  ›  专栏  ›  技术社区  ›  SimplGy

测试可观测值-检查对环境的副作用

  •  0
  • SimplGy  · 技术社区  · 6 年前

    我想从消费者的角度来测试可观察对象的行为方式。

    我不知道订阅(冷)或不订阅(热)时是否会有副作用。

    有没有办法在单元测试中验证这种行为?

    TestScheduler 从rxjs/连线测试,但我没有看到一个好方法来验证可观察对象的创建次数。

    // ...the create method has been mocked to emit after 3 frames
    const create$ = api.create(potato).pipe(
      tap(console.log.bind(null, 'object created'))
    );
    create$.subscribe();
    create$.subscribe();
    create$.subscribe();
    
    // Test how many times create$ has gotten a subscription, generated a cold observable, and completed.
    
    const timing = '---(a|)'; // wait 3 frames, emit value of `a`, complete
    const values = { a: potato };
    expectObservable(create$).toBe(timing, values);
    

    这个测试通过了,但是“objectcreated”消息触发了四次(三次用于我的订阅,一次来自中间件)。

    api.create .

    如何验证创建行为只执行一次?

    我试过:

    • spyOn
    • Array.isArray(create$.observers) --太间接了,只检查它是否热,而不检查它是否按预期运行。
    • tap(() => runCount++) expect(runCount).toBe(1) --如果我刷新调度程序,它就可以工作,但似乎超出了rxjs测试的标准。
    • 使用 Observable.create
    2 回复  |  直到 6 年前
        1
  •  0
  •   Ian MacDonald    6 年前

    我不确定我是否遵循了您的要求,但我可以解决您对您的可观察对象被创建多少次的担忧:

    const create$ = api.create(potato)
    

    .pipe 可观察对象的附件是从可观察对象点到订阅者的数据路径的一部分。

    potato ---(pipe)--->.subscribe()
         +----(pipe)--->.subscribe()
         +----(pipe)--->.subscribe()
         +----(pipe)--->(expectObservable inspection)
    

    相反,你可能想在那里放一根额外的管子来分享结果。也许毫不奇怪,这条管道被称为 share

    import { Observable, Subject } from 'rxjs';
    import { share, tap } from 'rxjs/operators';
    
    let obj: Subject<string> = new Subject<string>();
    let obs: Observable<string> = obj.pipe(tap(() => console.log('tap pipe')));
    
    obs.subscribe((text) => console.log(`regular: ${text}`));
    obs.subscribe((text) => console.log(`regular: ${text}`));
    obs.subscribe((text) => console.log(`regular: ${text}`));
    
    let shared: Observable<string> = obs.pipe(share());
    
    shared.subscribe((text) => console.log(`shared: ${text}`));
    shared.subscribe((text) => console.log(`shared: ${text}`));
    shared.subscribe((text) => console.log(`shared: ${text}`));
    
    obj.next('Hello, world!');
    

    输出

    tap pipe
    regular: Hello, world!
    tap pipe
    regular: Hello, world!
    tap pipe
    regular: Hello, world!
    tap pipe
    shared: Hello, world!
    shared: Hello, world!
    shared: Hello, world!
    
        2
  •  0
  •   SimplGy    6 年前

    这是迄今为止我发现的最好的方法,可以在单元测试中验证每个订阅只调用一次内部可观察对象。

    1. 创建一个模拟你实际操作的模拟冷观测
    2. 使用 spyOn
    3. 验证模拟冷态可观测数据上的运行计数
    scheduler.run(rx => {
      let runCount = 0;
      const timing = '---(a|)';
      const values = { a: {x:42} };
    
      // This represents the inner cold observable.
      // We want to validate that it does/does not get called once per subscription
      const mockedCreate$ = rx.cold(timing, values).pipe(
        tap(() => runCount++),
      );
      spyOn(api, 'doCreate').and.returnValue(mockedCreate$);
    
      const create$ = api.create({x:42}); // internally, calls doCreate
      create$.subscribe();
      create$.subscribe();
      create$.subscribe();
            // Explanation:
            // If api.create wasn't multicasting/sharing the result of the doCreate
            // operation, we'd see multiple actual save operations, not just 1
    
      rx.expectObservable(create$).toBe(timing, values);
      scheduler.flush();
      expect(runCount).toBe(1, 'how often the "real" create operation ran');
    });