【问题标题】:Test a function that contains a setTimeout()测试一个包含 setTimeout() 的函数
【发布时间】:2017-06-05 23:50:52
【问题描述】:

我的组件中有一个 close 函数,其中包含一个 setTimeout(),以便让动画有时间完成。

public close() {
    this.animate = "inactive"
    setTimeout(() => {
       this.show = false
    }, 250)
}

this.show 绑定到 ngIf

this.animate 绑定到动画。

我有一个测试需要测试这个功能

it("tests the exit button click", () => {
  comp.close()
  fixture.detectChanges()
  //verifies the element is no longer in the DOM
  const popUpWindow = fixture.debugElement.query(By.css("#popup-window"))
  expect(popUpWindow).toEqual(null)
})

当有setTimeout()时如何正确测试这个函数?

我使用的是jasmine.clock().tick(251),但窗口永远不会消失。对此也有什么想法吗?

【问题讨论】:

  • 您是否尝试过使用done
  • @jonrsharpe 究竟如何
  • 那么我建议你从那里开始你的研究;它用于测试异步进程。
  • 不是您最初要求的,而是使用 Angular 触发的 done 事件而不是使用超时。在您的模板中,您可以使用 <my-tag [@myAnimation]="animate" (@myAnimation.done)="show=false"> -- .done 事件代码将在您的动画完成后立即运行。

标签: angular testing jasmine settimeout karma-jasmine


【解决方案1】:

@Kailas 提到的这个解决方案对我有用:

hideToast() {
    setTimeout( () => {
      this.showToast = false;
    }, 5000);
  }

it('should hide toast', () => {
  component.showToast = true; // This value should change after timeout
  jasmine.clock().install();  // First install the clock
  component.hideToast();      // Call the component method that turns the showToast value as false
  jasmine.clock().tick(5000); // waits till 5000 milliseconds
  expect(component.showToast).toBeFalsy();  // Then executes this
  jasmine.clock().uninstall(); // uninstall clock when done
});

但有时您会收到此错误:

错误:Jasmine Clock 无法安装自定义全局计时器功能。时钟是否已安装?

要解决此问题,您需要先卸载时钟:

it('should hide toast', () => {
  jasmine.clock().uninstall()
  component.showToast = true; 
  jasmine.clock().install();  
  component.hideToast();      
  jasmine.clock().tick(5000); 
  expect(component.showToast).toBeFalsy();  
  jasmine.clock().uninstall(); 
});

【讨论】:

    【解决方案2】:

    我有一个与 OP 的设置非常相似的方法,所以我想我会添加我认为更简单的测试。

    ** 方法**

    public close() {
        this.animate = "inactive"
        setTimeout(() => {
           this.show = false
        }, 250)
    }
    

    ** 茉莉花测试 **

    it('should set show to "false" after 250ms when close is fired', (done) => {
          component.close();
          setTimeout(() => {
            expect(component.close).toBeFalse();
            done();
          }, 251);
        });
    

    注意使用 'done' 让 jasmine 知道测试已完成,并在我的方法的 setTimeout 之后向 setTimeout 添加 1 ms 以触发。

    【讨论】:

      【解决方案3】:

      刚刚在我的项目中尝试过这个并且工作正常:

      jasmine.clock().tick(10);
      

      【讨论】:

        【解决方案4】:

        你可以做以下两件事之一:

        1:在setTimeout()中实际等待测试250+1毫秒,然后检查元素是否真的消失了。

        2:使用fakeAsync()tick() 来模拟测试中的时间——tick() 将解析原始close() 中的setTimeout,并且检查可能在fixture.whenStable().then(...) 之后发生。

        例如:

        it("tests the exit button click", fakeAsync(() => {
          comp.close()
          tick(500)
          fixture.detectChanges()
        
          fixture.whenStable().then(() => {
            const popUpWindow = fixture.debugElement.query(By.css("#popup-window"))
            expect(popUpWindow).toBe(null)
          })
        }))
        

        我建议使用第二种方法,因为它比实际等待原始方法要快得多。如果你仍然使用第一个,请尝试降低测试前的超时时间,使其运行得更快。

        服务

        对于服务,您不需要在tick 之后调用detectChanges,也不需要在whenStable 中包装expect 语句。您可以在tick 之后立即执行您的逻辑。

          it('should reresh token after interval', fakeAsync(() => {
            // given
            const service: any = TestBed.get(CognitoService);
            const spy = spyOn(service, 'refreshToken').and.callThrough();
            ....
            // when
            service.scheduleTokenRefresh();
            tick(TOKEN_REFRESH_INTERVAL);
            // then
            expect(spy).toHaveBeenCalled();
          }));
        

        【讨论】:

        • 谢谢。第二个有效,但比简单地使用 tick() 稍微复杂一些。我必须将expects 包裹在fixture.whenStable().then( ... //expects ) 中。如果您允许我使用此附加信息更新您的答案,我将接受它作为正确答案。
        • 要补充这一点,您可能需要一个额外的fixture.detectChanges() 之前 tick。那么你可能根本不需要fixture.whenStable()(afaik fakeAsync 正是为了避免这种异步调用而设计的)。
        • 如果对其他人有帮助,调用 setTimeout 的函数需要在 fakeAsync 方法中调用(不能在 beforeEach 中调用)。看起来很明显,但它抓住了我!
        • 试试 jasmine.clock().tick(10);
        【解决方案5】:

        在我的组件中,方法是:

        hideToast() {
            setTimeout( () => {
              this.showToast = false;
            }, 5000);
          }
        

        对此进行测试(用 cmets 解释):

        it('should hide toast', () => {
          component.showToast = true; // This value should change after timeout
          jasmine.clock().install();  // First install the clock
          component.hideToast();      // Call the component method that turns the showToast value as false
          jasmine.clock().tick(5000); // waits till 5000 milliseconds
          expect(component.showToast).toBeFalsy();  // Then executes this
          jasmine.clock().uninstall(); // uninstall clock when done
        });
        

        希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 2018-04-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-17
          • 2022-01-02
          • 2015-08-21
          • 2013-03-18
          • 2020-10-18
          相关资源
          最近更新 更多