First off, there is a long list of similar questions (1, 2, 3, 4, 5, 6, 7, 8 and more), but none of those actually have an answer applicable to my case and many other have not been answered at all.
Description and links to source code
The following code is a simple Minimal, Reproducible Example of a much bigger project.
When running npm run test
from the project directory the
- expected result:
- all the tests to PASS without errors
- actual behavior:
- In Chromium the test commented below as
// FAILING TEST!
doesn't pass and reportUncaught Error: ViewDestroyedError: Attempt to use a destroyed view
(link to travis report in the real project) - In Google Chrome the tests pass, but if you open the console (F12) you see the same error being logged (so this also fails, but Chrome swallows it).
- In Chromium the test commented below as
Code
app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
hide: boolean = false;
someSubscription: Subscription;
constructor(private appServiceService: AppServiceService) { }
ngOnInit() {
this.someSubscription = this.appServiceService.shouldHide().subscribe(shouldHide => this.hide = shouldHide);
}
ngOnDestroy() {
this.someSubscription.unsubscribe();
}
}
app.component.html
<div class="row" id="jmb-panel" *ngIf="!hide">
Hello
</div>
app.component.spec
describe('AppComponent', () => {
let component: AppComponent;
let componentDe: DebugElement;
let fixture: ComponentFixture<AppComponent>;
const behaviorSubject = new BehaviorSubject<boolean>(false);
const appServiceStub = {
shouldHide: () => { spy.shouldHideSpyFn(); return behaviorSubject.asObservable() }
};
const spy = { shouldHideSpyFn: () => { } };
let spyShouldHide: jasmine.Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [{ provide: AppServiceService, useValue: appServiceStub }]
}).compileComponents();
}));
beforeEach(() => {
behaviorSubject.next(false);
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
componentDe = fixture.debugElement;
fixture.detectChanges();
spyShouldHide = spyOn(spy, 'shouldHideSpyFn');
});
it('should call AppServiceService#shouldHide on init', () => {
component.ngOnInit();
fixture.detectChanges();
expect(spyShouldHide).toHaveBeenCalledTimes(1);
});
it('should not render div if the AppServiceService#shouldHide observables emit true', () => {
appServiceStub.shouldHide().subscribe((li) => {
if (li) {
fixture.detectChanges();
expect(componentDe.query(By.css('#jmb-panel'))).toBeNull();
}
});
behaviorSubject.next(true);
});
// FAILING TEST!
it('should render div if the AppServiceService#shouldHide observables emit true', () => {
appServiceStub.shouldHide().subscribe((li) => {
if (!li) {
fixture.detectChanges();
expect(componentDe.query(By.css('#jmb-panel'))).not.toBeNull('Jumbotron panel should not be null');
}
});
behaviorSubject.next(false);
});
it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
});
Additional notes:
The order in which the tests is specified in the posted spec matters! If the tests order is changed all the tests may pass. This is not correct: all the tests should pass independently from the order they are specified. In fact, in the real project the tests are failing randomly: when the tests order established by jasmine is set like this. For this reason it wouldn't work for me to "fix" this by changing the tests order.
Question
Why does this error happen and what does it mean?, and more importantly,
How can we avoid/fix this error when implementing the tests in angular?