Angular 4多测试用例运行时触发ZoneAwareError问题求助
Let's break down why your second test fails when run alongside the first, and how to fix it:
The Root Cause
Your issue boils down to cross-test state pollution combined with a small misstep in async test handling:
- Your
SettingsServicemock is relying on a shared external object (settingsServiceMockSettings) that doesn't get reset between tests. When the first test runs, it leaves the mock in its default state, and by the time you update it in the second test, your component has already finished initializing and isn't picking up the change. - The
awaitonde.triggerEventHandlerisn't actually waiting for async logic in your component to finish—triggerEventHandlerdoesn't return a promise, so your assertions run before the component's double-click logic completes.
Step-by-Step Fixes
1. Isolate Mock State for Each Test
Stop using a shared configuration object. Instead, create a fresh mock setup inside your beforeEach so every test starts with a clean state:
describe('Component: ArticleComponent', () => { let httpService; let alertService; let stateService; let settingsService; let windowRefService; let fixture; let comp; let de; let el; let articleComponent; let article; // Assume this is defined elsewhere beforeEach(() => { // Create a fresh mock config for EVERY test const freshSettingsConfig = { openArticlesInCue: false // Default value }; // Initialize spies with fresh instances httpService = jasmine.createSpyObj('Http', ['get', 'post']); alertService = jasmine.createSpyObj('AlertService', ['show']); stateService = jasmine.createSpyObj('StateService', ['getCurrentState']); // Attach the fresh config to the settings service spy settingsService = jasmine.createSpyObj('SettingsService', [], freshSettingsConfig); windowRefService = jasmine.createSpyObj('WindowRefService', ['getWindow']); TestBed.configureTestingModule({ declarations: [ArticleFactoryComponent, ArticleComponent], imports: [BrowserAnimationsModule], providers: [ {provide: Http, useValue: httpService}, {provide: AlertService, useValue: alertService}, {provide: StateService, useValue: stateService}, {provide: WindowRefService, useValue: windowRefService}, {provide: SettingsService, useValue: settingsService}, ] }); // Initialize component with fresh mock state fixture = TestBed.createComponent(ArticleFactoryComponent); comp = fixture.componentInstance; comp.article = article; fixture.detectChanges(); de = fixture.debugElement.query(By.directive(ArticleComponent)); el = de.nativeElement; articleComponent = de.injector.get(ArticleComponent); });
2. Fix Async Test Handling
Replace await de.triggerEventHandler with fixture.whenStable()—this waits for all async operations in Angular's Zone to finish before running your assertions:
it('should open an article in CS on double click', async () => { const openArticleInCueSpy = spyOn(articleComponent, 'openArticleInCue').and.returnValue(undefined); const openArticleInCsSpy = spyOn(articleComponent, 'openArticleInCs').and.returnValue(undefined); // Trigger the double-click de.triggerEventHandler("dblclick", new MouseEvent("dblclick")); // Wait for all async logic in the component to complete await fixture.whenStable(); fixture.detectChanges(); expect(openArticleInCueSpy).not.toHaveBeenCalled(); expect(openArticleInCsSpy).toHaveBeenCalled(); });
3. Update Mock State Before Component Initialization
For your second test, update the mock setting before initializing the component to ensure the component picks up the new value on startup:
it('should open an article in CUE on double click', async () => { // Update the mock BEFORE creating the component settingsService.openArticlesInCue = true; // Re-initialize the component to use the updated mock fixture = TestBed.createComponent(ArticleFactoryComponent); comp = fixture.componentInstance; comp.article = article; fixture.detectChanges(); de = fixture.debugElement.query(By.directive(ArticleComponent)); articleComponent = de.injector.get(ArticleComponent); const openArticleInCueSpy = spyOn(articleComponent, 'openArticleInCue').and.returnValue(undefined); const openArticleInCsSpy = spyOn(articleComponent, 'openArticleInCs').and.returnValue(undefined); de.triggerEventHandler("dblclick", new MouseEvent("dblclick")); await fixture.whenStable(); fixture.detectChanges(); expect(openArticleInCueSpy).toHaveBeenCalled(); expect(openArticleInCsSpy).not.toHaveBeenCalled(); });
4. Optional: Reset TestBed Between Tests
If you still see state leaks, add an afterEach hook to fully reset the TestBed:
afterEach(() => { TestBed.resetTestingModule(); });
Key Takeaways
- Never share mutable mock state between tests—always create fresh mocks/configs in
beforeEach. - Use
fixture.whenStable()for async Angular tests instead of guessing withawaiton non-promise methods. - If you need to test a component with different mock states, update the mock before initializing the component to ensure it picks up the change.
内容的提问来源于stack exchange,提问作者Lukas

