Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular/Karma unit test error "1 timer(s) still in the queue"

This is hardly first encounter I've had with "1 timer(s) still in the queue", but usually I find some way to use tick() or detectChanges(), etc., to get out of it.

The test below was working fine until I tried to test for a condition that I know should throw an exception:

  it('should be able to change case', fakeAsync(() => {     expect(component).toBeTruthy();      fixture.whenStable().then(fakeAsync(() => {       component.case = 'lower';       fixture.autoDetectChanges();       tick(500);       const input = fixture.nativeElement.querySelector('input') as HTMLInputElement;       typeInElement('abcDEF', input);       fixture.autoDetectChanges();       tick(500);       expect(component.text).toEqual('abcdef');        component.case = 'upper';       fixture.autoDetectChanges();       tick(500);       typeInElement('abcDEF', input);       fixture.autoDetectChanges();       tick(500);       expect(component.text).toEqual('ABCDEF');        // Everything above works fine. Here's where the trouble begins       expect(() => {         component.case = 'foo';         fixture.autoDetectChanges();         tick(500);       }).toThrowError(/Invalid case attribute/);     }));   })); 

What I'm testing is an Angular component that's a wrapper around a Material input field. The component has many optional attributes, most of them just pass-through attributes for common input field features, but a few custom attributes too, like the one I'm testing above for upper-/lowercase conversion.

The acceptable values for the case attribute are upper, lower, and mixed (with empty string, null, or undefined treated as mixed). The component should throw an exception for anything else. Apparently it does, and the test succeeds, but along with the success I get:

ERROR: 'Unhandled Promise rejection:', '1 timer(s) still in the queue.', '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', Error: 1 timer(s) still in the queue. Error: 1 timer(s) still in the queue.    ... 

Can anyone tell me what I might be doing wrong, or a good way to flush out lingering timers?

Disclaimer: A big problem when I go looking for help with Karma unit tests is that, even when I explicitly search for "karma", I mostly find answers for Pr0tractor, Pr0tractor, and more Pr0tractor. This isn't Pr0tractor! (Deliberately misspelled with a zero so it doesn't get search matches.)

UPDATE: I can work around my problem like this:

      expect(() => {         component.inputComp.case = 'foo';       }).toThrowError(/Invalid camp-input case attribute/); 

This isn't as good of a test as assigning the (bad) value via an HTML attribute in the test component's template, because I'm just forcing the value directly into the component's setter for the attribute itself, but it'll do until I have a better solution.

like image 425
kshetline Avatar asked Oct 16 '19 15:10

kshetline


People also ask

What is fakeAsync in Angular testing?

fakeAsynclinkWraps a function to be executed in the fakeAsync zone: Microtasks are manually executed by calling flushMicrotasks() . Timers are synchronous; tick() simulates the asynchronous passage of time.

What is fixture detectChanges ()?

fixture is a wrapper for our component's environment so we can control things like change detection. To trigger change detection we call the function fixture.detectChanges() , now we can update our test spec to: Copy it('login button hidden when the user is authenticated', () => { expect(el. nativeElement.

What is karma in unit testing?

Karma is a node-based test tool that allows you to test your JavaScript codes across multiple real browsers. A node-based tool is any tool that needs the Nodejs engine installed for it to run and can be accessed (installed) through the node package manager (npm).

What is karma in Angular testing?

Karma is a task runner for our tests. It uses a configuration file in order to set the startup file, the reporters, the testing framework, the browser among other things. The rest of the dependencies are mainly reporters for our tests, tools to use karma and jasmine and browser launchers.


2 Answers

I have faced with the similar problem. The solution was flush function usage.

import { fakeAsync, flush } from '@angular/core/testing';  it('test something', fakeAsync(() => {    // ...    flush(); })); 
like image 119
SternK Avatar answered Sep 17 '22 14:09

SternK


I ran into the same issue recently - to resolve I called discardPeriodicTasks() -from @angular/core/testing at the end of my it function and my tests passed after that.

In this scenario you may want to insert it before your final expect

 it('should be able to change case', fakeAsync(() => {     expect(component).toBeTruthy();      fixture.whenStable().then(fakeAsync(() => {       component.case = 'lower';       fixture.autoDetectChanges();       tick(500);       const input = fixture.nativeElement.querySelector('input') as HTMLInputElement;       typeInElement('abcDEF', input);       fixture.autoDetectChanges();       tick(500);       expect(component.text).toEqual('abcdef');        component.case = 'upper';       fixture.autoDetectChanges();       tick(500);       typeInElement('abcDEF', input);       fixture.autoDetectChanges();       tick(500);       expect(component.text).toEqual('ABCDEF');        discardPeriodicTasks() <-------------------- try here        // Everything above works fine. Here's where the trouble begins       expect(() => {         component.case = 'foo';         fixture.autoDetectChanges();         tick(500);       }).toThrowError(/Invalid case attribute/);            })); 

tick acts to move the time forward in your fakeAsync context.

flush acts to simulate the completion of time in that context by draining the macrotask queue till it is empty.

discardPeriodicTasks "throws out" any remaining periodic tasks.

They each serve different purposes and will have different use cases.

like image 37
brooklynDadCore Avatar answered Sep 19 '22 14:09

brooklynDadCore