Repository navigation
Unit test within async using fixture.detectChanges() fails #89
Copy link
Copy link
Open
Description
Hello,
This is the issue description:
Using the method from ComponentFixture .detectChanges() is failing when you use it within an async() unit test.
How to reproduce:
- Clone this repo:
git clone https://github.com/ionic-team/ionic-unit-testing-example.git - Install packages:
cd ionic-unit-testing-example && sudo npm install - Add the following unit test to the
src/app/app.component.spec.ts:
it( 'example test async with fixture.detectChanges()', async(() => {
fixture.detectChanges();
}));- Run tests:
npm run test-ci
After running the tests, it fails with the following error:
..ERROR: 'Unhandled Promise rejection:', 'invalid link: Page1', '; Zone:', 'angular', '; Task:', 'Promise.then', '; Value:', 'invalid link: Page1', undefined
ERROR: 'Unhandled Promise rejection:', 'invalid link: Page1', '; Zone:', 'angular', '; Task:', 'Promise.then', '; Value:', 'invalid link: Page1', undefined
Chrome 65.0.3325 (Linux 0.0.0) MyApp Component example test async with fixture.detectChanges() FAILED
Failed: Uncaught (in promise): invalid link: Page1
Error: Uncaught (in promise): invalid link: Page1
at resolvePromise (webpack:///node_modules/zone.js/dist/zone.js:824:0 <- test-config/karma-test-shim.js:117151:31)
at Object.reject (webpack:///node_modules/zone.js/dist/zone.js:746:0 <- test-config/karma-test-shim.js:117073:17)
at NavControllerBase._fireError (webpack:///node_modules/ionic-angular/navigation/nav-controller-base.js:223:0 <- test-config/karma-test-shim.js:51342:16)
at NavControllerBase._failed (webpack:///node_modules/ionic-angular/navigation/nav-controller-base.js:216:0 <- test-config/karma-test-shim.js:51335:14)
at webpack:///node_modules/ionic-angular/navigation/nav-controller-base.js:263:44 <- test-config/karma-test-shim.js:51382:59
at ZoneDelegate.invoke (webpack:///node_modules/zone.js/dist/zone.js:392:0 <- test-config/karma-test-shim.js:116719:26)
............
Expected result should be at least not to throw this error when using fixture.detectChanges().
The full src/app/app.component.spec.ts file would be as the following:
import { async, TestBed, fakeAsync, ComponentFixture, tick } from '@angular/core/testing';
import { IonicModule, Platform, MenuController, Menu } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { MyApp } from './app.component';
import {
PlatformMock,
StatusBarMock,
SplashScreenMock
} from '../../test-config/mocks-ionic';
import { Page } from 'ionic-angular/navigation/nav-util';
import { By } from '@angular/platform-browser';
describe('MyApp Component', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyApp],
imports: [
IonicModule.forRoot(MyApp)
],
providers: [
{ provide: StatusBar, useClass: StatusBarMock },
{ provide: SplashScreen, useClass: SplashScreenMock },
{ provide: Platform, useClass: PlatformMock }
]
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyApp);
component = fixture.componentInstance;
});
it('should be created', () => {
expect(component instanceof MyApp).toBe(true);
});
it('should have two pages', () => {
expect(component.pages.length).toBe(2);
});
it( 'example test async with fixture.detectChanges()', async(() => {
fixture.detectChanges();
}));
});Doest anyone at least know why this could be happenning? I don't know if this could even be an Angular bug with lazy loading or whatever.
The only reference to Page1 is inside src/app/app.component.ts in order to set up the page as the rootPage of the app:
import { Component, ViewChild } from '@angular/core';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { Nav, Platform, Menu } from 'ionic-angular';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = 'Page1';
pages: Array<{ title: string, component: any }>;
constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) {
// used for an example of ngFor and navigation
this.pages = [
{ title: 'Page One', component: 'Page1' },
{ title: 'Page Two', component: 'Page2' }
];
}
ionViewDidLoad() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
openPage(page) {
// Reset the content nav to have just this page
// we wouldn't want the back button to show in this scenario
this.nav.setRoot(page.component);
}
}Thank you in advance.
Regards,
David.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
neilbo commentedon Jun 8, 2018
Have you tried using 'fakeAsync' with 'flushMicrotasks' syntax?
188599 commentedon Nov 13, 2020
Not sure if entirely related to this, but I feel my issue is similar enough.
I have a test case where I need to wait for a promise to complete before executing my test. So I have a
beforeEachthat uses theasyncmethod to execute this promise. The thing is, the test I'm trying to execute is running before the promise in thebeforeEachfinishes.After lots of fiddling around with the methods, I realized that commenting the method call to
ComponentFixture.detectChangesmakes it so thebeforeEachis properly awaited as intended.Here's the code I'm trying to execute:
I also tried to simplify things by just doing a simple promise test:
Which also fails. Again, removing the
detectChangesmakes the test pass.I'm guessing something really wonky happens when calling
detectChanges, very likely related to this particular component (since I have a very similar test that works perfectly), but I'm having a very difficult time to pinpoint what it is exactly since there no errors being throw at all.188599 commentedon Nov 13, 2020
When I wrote the previous post I thought I was in the angular repository. 🤦
Anyways, turns that just changing to a proper
async/awaitfunction works.Change my previous code to this:
And everything works as intended.
lopes-leandro commentedon Aug 13, 2022
Hello everyone!!!
I recently ran into the same problem reported above, while testing one of the components in my current project.
After pounding my head around for o while, I found that the fixture.detectChanges() method triggers application lifecycle methods.
In my case I have the ngInit method that was called before the execution of the first test case and it generated an exception when calling a service.
I removed the fixture.detectChanges() method from inside beforeEach and put it inside the first test case. When running the test, the execution did not throw any more exceptions.
patelabhishek5232 commentedon Apr 19, 2023
Hello ,I did try your suggestion but still getting same error ,does any one have other suggestion