angular: Error during cleanup of component: Display error too

I’m submitting a … (check one with “x”)

[ ] bug report => search github for a similar issue or PR before submitting
[X] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior In TestBed.resetTestingModule() an error may occur. The error is not logged to the console, only the component that failed to be destroyed is logged.

Expected behavior The console.error at this line https://github.com/angular/angular/blob/master/packages/core/testing/src/test_bed.ts#L252 will also log the error.

What is the motivation / use case for changing the behavior? Currently I cannot investigate why I get this error, I’ve disabled all my ngOnDestroy’s but still getting this error.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 34
  • Comments: 24 (2 by maintainers)

Most upvoted comments

In my case, I was trying to unsubscribe from an undefined subscription in ngOnDestroy. Just verifying non-null subscription did the trick:

if (this.subscription) {
  this.subscription.unsubscribe();
}

Solved by adding this to my unit tests:

afterEach(() => {
   component.ngOnDestroy();
});

But I’m not sure about why it solves it…

I’m getting the very same error, I’ve tried to unsubscribe() all subscriptions in ngOnDestroy as well as adding fixtures.detectChanges() after createComponent() method in the test case.

I also wont get any error if I fit a test but the error appears again if I fit two tests, so it seems that it has something when running multiple test cases of the same component.

+1. My builds are failing because the unit tests are ran upon a code commit. The command that is run by the build step is ng test --sourcemaps=true --watch=false --single-run=true --browsers=PhantomJS

However, running ng test shows no unit test failures and everything passes.

image

image

This doesn’t fix the issue, but until then you can get somewhat better context of the error if you open the devtools in the karma window

This StackOverflow answer helped my resolve the issue: https://stackoverflow.com/a/44186041/667773

It says the following:

The “Error during component cleanup” error message happens because when ngOnDestroy() is called, this.routeSubscription is undefined. This happens because ngOnInit() was never invoked, meaning that you never subscribed to the route. As described in the Angular testing tutorial, the component isn’t initialized fully until you call fixture.detectChanges() the first time.

Therefore, the correct solution is to add fixture.detectChanges() to your beforeEach() block right after the createComponent is called. It can be added any time after you create the fixture. Doing so will ensure that the component is fully initialized, that way component cleanup will also behave as expected.

This is not an Angular Problem. Nor is it the component code. This is your test code.

Adding a “just if the subscription exists” in you ngOnDestroy actually is not the right way to fix this, your tests fail because you set up the tests in a wrong way.

In most cases you try to assign a subscription to a variable. When this code is not being called by your tests, the error occurs. Expected behaviour.

Call the code.

Mostly - when this is in ngOnInit, then you need to call fixture.detectChanges() to kick of the Change Detection Lifecycle.

Incase it can help someone in the future… I found the issue and the solution like following:

Error was : Error during cleanup of component: parentComponent

I was testing a (child) component which its router settings in the test file configured like such:

So like this it gives error:

export const routes: Routes [
    {
        path : '', component: parentComponent,
        children: [
            {path: 'child-path/:param', component: childComponent}
        ]
   }
];

// test configuration
... 
TestBed.configureTestingModule({
    declarations: [childComponent],
    imports: [RouterTestingModule.withRoutes(routes)]
});

beforeEach(() => {
    fixture = TestBed.createComponent(childComponent);
    component = fixture.componentInstance;
});

// Test
 it('should get the param, () => {
        router.navigate(['child-path/someParam']).then((res) => { 
            fixture.detectChanges(); 
            expect(component.urlParam).toEqual('someParam');
        }); 
});

Simply removing parent fixes the issue and that is also logical because we are testing childComponent. I guess as you declare a parent component and navigate to the child, the parent component will still be loaded but won’t be initialized so accordingly can’t be destroyed either… Solution

export const routes: Routes [
    {path: 'child-path/:param', component: childComponent}
];

The #22162 Pull Request enables you to see the route cause error when you have the console error “Error during cleanup of component”. You will now be able to see the stacktrace when the fixture can’t be destroyed for x reason.

The main cause was that the error in the catch when fixture can’t be destroyed was not send to user.

Hope it helps you all !

Case

I face this problem with the following configuration:

Original service

@Injectable()
export class MyService {
  doSomething(){
    ...
  }
}

Mock service to be used in the tests

@Injectable()
export class MyServiceMock {}

My component

@Component(...)
export class MyComponent {
  constructor(private myService: MyService ) {}
...
  ngOnDestroy() {
    this.myService.doSomething();
  }
}
beforeEach(async (() => {
    TestBed.configureTestingModule({
      declarations: [
        MyComponent,
      ],
      providers: [
        MyService, {provide: MyService, useValue: new MyServiceMock()},
      ],
    });
  }));

beforeEach(async() => {
    await TestBed.compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

after runs the tests I received the following message:

Error during cleanup of component MyComponent {
  MyService: MyServiceMock {}
  ...
}

So, I realized that something was wrong in the lifecycle then I did it:

  afterEach(() => {
    component.ngOnDestroy();
  });

then I received the following message after run the tests:

 TypeError: this.myService.doSomething is not a function
 ngOnDestroy() {
    this.myService.doSomething();
  }
}

At this moment I realized that the problem occurs when you have added the ngOnDestroy lifecycle in your component and the method tries to call something that its not defined.

Solution

  • Always check before destroy example:
ngOnDestroy() {
  if (this.myService && this.myService.doSomething) {
    this.myService.doSomething();
  }
}

I can also confirm this error. All subscriptions are unsubscribed on destroy. Also I can confirm that it only seems to happen when more than one test is run on the same component test suite.

I get this error even though I unsubscribe to all observable correctly