【问题标题】:How to test Angular component with Observable Input如何使用 Observable Input 测试 Angular 组件
【发布时间】:2018-05-24 14:35:15
【问题描述】:

我正在尝试测试一个 Angular Component,它基本上接收一个 Observable 并根据从该 Observable 发出的值更改其 template。这是一个简化版:

@Component({
    selector: 'async-text',
    template: `
        <span>{{ text | async }}</span>
    `,
})
export class AsyncTextComponent {    
    @Input() text: Observable<string>;
}

我想测试一下,目前这就是我所拥有的,使用rxjs-marbles(虽然不是必须的)。

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AsyncTextComponent } from './async-text.component';

describe('AsyncTextComponent', () => {
  let component: BannerComponent;
  let fixture: AsyncTextComponent<AsyncTextComponent>;

  it('...',
    marbles(m => {
      fixture = TestBed.createComponent(AsyncTextComponent);
      component = fixture.componentInstance;
      component.text = m.cold('-a-b-c|', {
        a: 'first',
        b: 'second',
        c: 'third',
      });

      fixture.detectChanges();
      expect(component.nativeElement.innerHTML).toContain('first');

      fixture.detectChanges();
      expect(component.nativeElement.innerHTML).toContain('second');

      fixture.detectChanges();
      expect(component.nativeElement.innerHTML).toContain('third');
    })
  );
});

显然这行不通。我的问题是我没有找到一种方法在每个 expect 之间将 TestScheduler 推进给定数量的“帧”。

如何手动跳帧?或者,是否有更好的方法来测试上述组件/场景(接收 Observable 的 Angular 组件,我想根据 Observable 的发射测试它的行为)。

我确实看到了.flush(),但据记录,它运行所有 Observable 的发射,所以我会到达最终状态,并且无法测试状态之间的不同转换。

谢谢

【问题讨论】:

  • 你不应该将 observable 作为输入传递
  • 去掉复杂性,在父组件中使用异步管道
  • 以上是简化的组件。实际上,它不仅仅是显示传入的文本,而是接收Observable&lt;Flag&gt;,并根据发出的Flag 的值显示不同的模板。例如,Flag.Loading 将显示一个加载指示器,当发出 Flag.Complete 时,会显示另一个模板。
  • 好的。不会改变调用子组件时应该在父组件上使用异步的事实,因此它只获取值。
  • 我原则上同意,但实际上这会增加围绕该组件的样板文件,因为您必须将引发错误或完成的 Observable 转换为其中一个 Flags,然后才可以通过它。

标签: angular rxjs angular-test rxjs6


【解决方案1】:

您不必使用任何库来测试它。更重要的是,您可以在 Angular 的上下文之外对其进行测试。

不管怎样,这里是解释。

要对此进行测试,我建议使用变量。但如果你想坚持你的方法,你应该坚持下去。

it('should display first', done => {
  // Mock your component
  component.text = Observable.of('first');
  // Detect template changes
  fixture.detectChanges();
  // trigger a change detection, just in case (try without, you never know)
  setTimeout(() => {
    // Get the element that is displaying (tip: it's not your whole component)
    const el = fixture.nativeElement.querySelector('span');
    // Test the innet text, not the HTML
    // Test with includes, in case you have spaces (but feel free to test without includes)
    expect(el.innerText.includes('first')).toEqual(true);
    // End your test
    done();
  });
});

【讨论】:

  • 你应该使用 fakeAsync 并勾选。没有什么能保证你在 setTimeout 期间会发出 observable。另外,如果 observable 在 10 秒后发出,你会设置一个 10 秒的 setTimeout 吗?你的测试需要很长时间。使用 fakeAsync,您可以控制时间,它会是即时的
  • 这不涉及测试状态变化。在我的 OP 中看到我想测试当'first' 发出时模板显示“第一”,当'second' 发出时模板显示“第二”等等。
  • @maxime1992 Observable 被模拟了。您已经可以控制发射。至于OP,我写了逻辑,我不会写整个测试,我很确定你可以自己解决!
猜你喜欢
  • 2020-01-23
  • 2021-08-13
  • 2017-09-14
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-04
  • 1970-01-01
相关资源
最近更新 更多