Testowanie elementu kątowego z błędem anulowania subskrypcji podczas czyszczenia komponentu
Testuję komponent, który subskrybuje params routera. Każda próba i wszystko działa dobrze. Ale jak zaglądam do konsoli to widzę błąd:
Wiesz, dlaczego tak się dzieje?Błąd podczas czyszczenia aplikacji komponentu Viewcomponent lokalkonsole.(funkcja anonimowa) @ context.js: 232
Próbowałem usunąć unsubscribe()
z metody ngOnDestroy()
i błąd znika.
Czy karma / jaśmin wspiera unsubscribe()
automatycznie?
Oto komponent i testy
Komponent
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx'
import { AppService } from 'app.service';
@Component({
selector: 'app-component',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
private routeSubscription: Subscription;
// Main ID
public applicationId: string;
constructor(
private route: ActivatedRoute,
private _service: AppService
) { }
ngOnInit() {
this.routeSubscription = this.route.params.subscribe(params => {
this.applicationId = params['id'];
this.getDetails();
this.getList();
});
}
getDetails() {
this._service.getDetails(this.applicationId).subscribe(
result => {
console.log(result);
},
error => {
console.error(error);
},
() => {
console.info('complete');
}
);
}
getList(notifyWhenComplete = false) {
this._service.getList(this.applicationId).subscribe(
result => {
console.log(result);
},
error => {
console.error(error);
},
() => {
console.info('complete');
}
);
}
ngOnDestroy() {
this.routeSubscription.unsubscribe();
}
}
Component Spec file
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
async,
fakeAsync,
ComponentFixture,
TestBed,
tick,
inject
} from '@angular/core/testing';
import {
RouterTestingModule
} from '@angular/router/testing';
import {
HttpModule
} from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Router, ActivatedRoute } from '@angular/router';
// Components
import { AppComponent } from './app.component';
// Service
import { AppService } from 'app.service';
import { AppServiceStub } from './app.service.stub';
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let service: AppService;
let expectedApplicationId = 'abc123';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [RouterTestingModule, HttpModule],
providers: [
FormBuilder,
{
provide: ActivatedRoute,
useValue: {
params: Observable.of({id: expectedApplicationId})
}
},
{
provide: AppService,
useClass: AppServiceStub
}
],
schemas: [ NO_ERRORS_SCHEMA ]
})
.compileComponents();
}));
tests();
});
function tests() {
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
service = TestBed.get(AppService);
});
/*
* COMPONENT BEFORE INIT
*/
it(`should be initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
/*
* COMPONENT INIT
*/
it(`should retrieve param id from ActivatedRoute`, async(() => {
fixture.detectChanges();
expect(comp.applicationId).toEqual(expectedApplicationId);
}));
it(`should get the details after ngOnInit`, async(() => {
spyOn(comp, 'getDetails');
fixture.detectChanges();
expect(comp.getDetails).toHaveBeenCalled();
}));
it(`should get the list after ngOnInit`, async(() => {
spyOn(comp, 'getList');
fixture.detectChanges();
expect(comp.getList).toHaveBeenCalled();
}));
}
Serwis.stub
import { Observable } from 'rxjs/Observable';
export class AppServiceStub {
getList(id: string) {
return Observable.from([
{
id: "7a0c6610-f59b-4cd7-b649-1ea3cf72347f",
name: "item 1"
},
{
id: "f0354c29-810e-43d8-8083-0712d1c412a3",
name: "item 2"
},
{
id: "2494f506-009a-4af8-8ca5-f6e6ba1824cb",
name: "item 3"
}
]);
}
getDetails(id: string) {
return Observable.from([
{
id: id,
name: "detailed item 1"
}
]);
}
}
10 answers
Komunikat o błędzie "Error during component cleanup" występuje, ponieważ gdy wywołane jest ngOnDestroy()
, this.routeSubscription
jest niezdefiniowane. Dzieje się tak, ponieważ ngOnInit()
nigdy nie został wywołany, co oznacza, że nigdy nie subskrybowałeś trasy. Jak opisano w Angular Testing tutorial, komponent nie jest w pełni zainicjalizowany, dopóki nie wywołasz fixture.detectChanges()
za pierwszym razem.
Dlatego poprawnym rozwiązaniem jest dodanie fixture.detectChanges()
do bloku beforeEach()
zaraz po wywołaniu createComponent
. Można go dodać w dowolnym momencie po utworzeniu oprawa. W ten sposób komponent zostanie w pełni zainicjowany, w ten sposób oczyszczanie komponentu będzie również zachowywać się zgodnie z oczekiwaniami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-10 14:38:37
Musisz refaktor swojej metody ngOnDestroy jak poniżej:
ngOnDestroy() {
if ( this.routeSubscription)
this.routeSubscription.unsubscribe();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 13:28:28
Więc Moja sytuacja była podobna, ale nie do końca taka sama: umieszczam to tutaj na wypadek, gdyby ktoś inny uznał to za pomocne. Podczas testów jednostkowych z Jamine / Karma otrzymywałem
'ERROR: 'Error during cleanup of component','
Okazało się, że to dlatego, że nie obsługiwałem poprawnie moich obserwatorów i nie mieli na nich funkcji błędu. Więc poprawka dodawała funkcję błędu:
this.entityService.subscribe((items) => {
///Do work
},
error => {
this.errorEventBus.throw(error);
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-18 11:40:00
Jestem w podobnej sytuacji, w której chcę przetestować funkcję w moim komponencie poza kontekstem samego komponentu.
To mi się udało:
afterEach(() => {
spyOn(component, 'ngOnDestroy').and.callFake(() => { });
fixture.destroy();
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-23 17:35:14
Dodanie do odpowiedzi @ David Brown poniższy kod działa dla mnie.
.subscribe(res => {
...
},
error => Observable.throw(error)
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 12:46:55
W moim przypadku zniszczenie komponentu po każdym teście rozwiązało problem. Możesz więc spróbować dodać to do swojej funkcji opisującej:
afterEach(() => {
fixture.destroy();
})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-27 13:32:53
Musisz zrobić 2 rzeczy, aby rozwiązać ten błąd.
1 - dodaj oprawę.detectChanges (); in beforeEach ()
2 - musisz dodać poniżej, aby komponent mógł być jasny.
afterEach(() => {
fixture.destroy();
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-17 04:38:04
Jak wyjaśnia @randomPoison, błąd jest wyzwalany, gdy komponent, który używa unsubscribe
, nie jest inicjowany. Jednak wywołanie {[2] } jest rozwiązaniem, gdy błąd znajduje się w pliku spec danego komponentu.
Ale możemy również mieć do czynienia z FooComponent
, który tworzy BarComponent
i BarComponent
używa unsubscribe
w swoim ngOnDestroy
. Trzeba zrobić porządne wyśmiewanie.
Sugerowałbym inne podejście do czyszczenia subskrypcji, takie, które jest deklaratywne i nie spowoduje takich problemów. Oto przykład:
export class BazComponent implements OnInit, OnDestroy {
private unsubscribe$ = new Subject();
ngOnInit(): void {
someObservable$
.pipe(takeUntil(this.unsubscribe$))
.subscribe(...);
}
ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}
Więcej o tym podejściu tutaj
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-17 15:50:34
W moim przypadku błąd był w szablonie. Wystąpił błąd w komponencie potomnym ngDestroy, który nie został zniszczony, ponieważ próbowałem ustawić właściwość readonly. Warto byłoby sprawdzić, czy komponenty dziecka są właściwie niszczone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-19 14:39:36
Dla mnie to, co naprawiło ten błąd, było wewnątrz ngOnDestroy mojego komponentu, zapakowałem wysyłkę do sklepu i wypisałem się z subskrypcji w spróbuj złapać.
ngOnDestroy(): void {
try {
this.store.dispatch(new foo.Bar(this.testThing()));
if(this.fooBarSubscription) {
this.fooBarSubscription.unsubscribe();
}
} catch (error) {
this.store.dispatch(new foo.Bar(this.testThing()));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-12 15:43:56