如何对 Rxjs 的 combineLatest 进行单元测试?

huangapple go评论69阅读模式
英文:

How to unit test Rxjs combineLatest?

问题

我的组件的ngOnInit方法如下:

public ngOnInit() {
    this.ctrSubscription = combineLatest(this.route.queryParams, this.tags$).subscribe(async params => {
      if (Object.keys(params[0]).length > 0) {
        this.initView({ ...params[0] });
      }
      if (Object.keys(params[1]).length > 0) {
        this.wsTags = Object.values(params[1]);
      }
      if (params[0] && this.wsTags.length > 0) {
        this.searchWs({ ...params[0] }.q);
        this.pruneData();
      }
    });
  }

这是我如何进行测试的部分:

it('should perform search and load the results', fakeAsync(() => {
    TestBed.get(ActivatedRoute).queryParams = of({ q: 'test' });
    const initSpy = spyOn<any>(component, 'initView').and.callThrough();
    const subSpy = spyOn<any>(component.tags$, 'subscribe');
    component.wsTags = ensureState(mockTags as any);
    flushMicroTasks();
    fixture.detectChanges();
    flushMicroTasks();
    expect(initSpy).toHaveBeenCalledWith({ q: 'test' });
    expect(subSpy).toHaveBeenCalled();
    expect(component.wsTags).toBe(Object.values(mockTags));
  }));

间谍(spies)根本没有被调用。我在运行测试时设置了断点,以了解是否正确传递了值,我能够正确捕获传递的查询参数,但条件仍然失败。

我尝试查看文档以更好地理解,并在 Stack Overflow 上查看各种类似的问题,但我无法解决它。我对编写单元测试相对不熟悉,所以任何帮助将不胜感激。谢谢。

英文:

My component's ngOnInit does this

public ngOnInit() {
    this.ctrSubscription = combineLatest(this.route.queryParams, this.tags$).subscribe(async params =&gt; {
      if (Object.keys(params[0]).length &gt; 0) {
        this.initView({ ...params[0] });
      }
      if (Object.keys(params[1]).length &gt; 0) {
        this.wsTags = Object.values(params[1]);
      }
      if (params[0] &amp;&amp; this.wsTags.length &gt; 0) {
        this.searchWs({ ...params[0] }.q);
        this.pruneData();
      }
    });
  }

And this is how i am testing it

  it(&#39;should perform search and load the results&#39;, fakeAsync(() =&gt; {
    TestBed.get(ActivatedRoute).queryParams = of({ q: &#39;test&#39; });
    const initSpy = spyOn&lt;any&gt;(component, &#39;initView&#39;).and.callThrough();
    const subSpy = spyOn&lt;any&gt;(component.tags$, &#39;subscribe&#39;);
    component.wsTags = ensureState(mockTags as any);
    flushMicroTasks();
    fixture.detectChanges();
    flushMicroTasks();
    expect(initSpy).toHaveBeenCalledWith({ q: &#39;test&#39; });
    expect(subSpy).toHaveBeenCalled();
    expect(component.wsTags).toBe(Object.values(mockTags));
  }));

the spys are not getting called at all.I put a breakpoint when running tests to know if the value is passing or not , and i was able to catch the query params being passed correctly but still the if fails.

I tried going through the docs to understand better and also various similar questions here on stack overflow, i couldn't able to solve it. I am fairly new to writing unit tests, so any help will be appreciated. Thanks.

答案1

得分: 1

一个潜在的问题是你在it块内部对ActivatedRoute进行了模拟。这太晚了,你的ngOnInit在第一次compileComponents之后的detectChanges之后运行。

在配置测试模块时,请尝试:

describe('测试描述', () => {
  let component: YourComponent;
  let activatedRoute: ActivatedRoute;
  let fixture: ComponentFixture<YourComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [YourComponentToTest,
        // 如果需要,可以添加其他声明。
      ],
      providers: [
        {
          provide: ActivatedRoute,
          useValue: {
            queryParams: of({ q: 'test' }),
          },
        },
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(YourComponentToTest);
    component = fixture.componentInstance;
    activatedRoute = TestBed.get(ActivatedRoute);
    // ngOnInit 现在已经运行 - 在第一次 detectChanges 之后
    fixture.detectChanges();
  });

  it('应该执行你想要的操作', () => {
    // 你的安排、行动和断言。
  });
})

确保this.tags$也被初始化。

我是全程手写的,可能会有拼写错误。

英文:

One potential issue is that you're mocking ActivatedRoute within the it block. This is too late, your ngOnInit runs after the first detectChanges after compileComponents.

When configuring your testing module, try:

describe(&#39;Put description of the test&#39;, () =&gt; {
  let component: YourComponent;
  let activatedRoute: ActivatedRoute;
  let fixture: ComponentFixture&lt;YourComponent&gt;;

  beforeEach(() =&gt; {
    TestBed.configureTestingModule({
     declarations: [ YourComponentToTest, 
      // other declarations if need be.
     ],
     providers: [
      provide: ActivatedRoute,
      useValue: {
       queryParams: of({ q: &#39;test&quot; }),
      }
     ]
   }).compileComponents();
  });
  
  beforeEach(() =&gt; {
    fixture = TestBed.createComponent(YourComponentToTest);
    component = fixture.componentInstance;
    activatedRoute = TestBed.get(ActivatedRoute);
    // ngOnInit is ran now -- after the first detectChanges
    fixture.detectChanges();
  });

  it(&#39;should do what you want&#39;, () =&gt; {
   // your arrange, act, and assertions.
  });
})

Make sure this.tags$ is initialized as well.

I did all of this freehand so there may be typo(s).

huangapple
  • 本文由 发表于 2020年1月6日 20:42:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/59612335.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定