feat(core): support global fakeAsync functionality across multiple test cases. #40611
+769
−5
Conversation
What happens when developer forgets to invoke An alternative approach may be something as shown below. Have you consider it? What do you think are advantages/disadvantages of the two approaches?
|
@mhevery, yeah, I like your idea,
I will update the PR |
ee3ccbb
to
b599041
b599041
to
c36741a
aio/content/examples/testing/src/app/auth/auth.component.spec.ts
Outdated
Show resolved
Hide resolved
aio/content/examples/testing/src/app/auth/auth.component.spec.ts
Outdated
Show resolved
Hide resolved
aio/content/examples/testing/src/app/auth/auth.component.spec.ts
Outdated
Show resolved
Hide resolved
By the way, should the hooks be |
c36741a
to
17582fd
@petebacondarwin , yes, you are right, |
72fba02
to
56fd0d6
…ach()` hooks functions. Close #40387 Wrap the `fakeAsync()` with the `beforeEach()/afterEach()` hooks. For example, we have a Component like this. @component({...}) export class AppComponent { timerId: number; ngOnInit() { this.timerId = setTimeout(() => {}); } ngOnDestroy() { clearTimeout(this.timerId); } } And without hook functions, we need to write test like this. describe('AppComponent test', () => { let fixture: ComponentFixture<AppComponent>; beforeEach(() => { ... fixture = TestBed.createComponent(AppComponent); }); it('test case1', fakeAsync(() => { fixture.detectChanges(); // do some test with fixture fixture.destroy(); })); it('test case2', fakeAsync(() => { fixture.detectChanges(); // do some test with fixture fixture.destroy(); })); }); We need to call `fixture.destroy()` inside each tests, since each `it()` use it's own fakeAsync() and we need to clean up the timerId created in that FakeAsyncZone. With the hook functions, we can write case in this way. describe('AppComponent test', () => { let fixture: ComponentFixture<AppComponent>; const fakeAsyncWithFixture = fakeAsync.wrap({ beforeEach: () => { fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); } afterEach: () => fixture.destroy(); }); it('test case1', fakeAsyncWithFixture(() => { // do some test with fixture })); it('test case2', fakeAsyncWithFixture(() => { // do some test with fixture })); }); Also the wrap() function support nesting. describe('AppComponent test', () => { let fixture: ComponentFixture<AppComponent>; const fakeAsyncWithFixture = fakeAsync.wrap({ beforeEach: () => { fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); } afterEach: () => fixture.destroy(); }); it('test case1', fakeAsyncWithFixture(() => { // do some test with fixture })); it('test case2', fakeAsyncWithFixture(() => { // do some test with fixture })); describe('AppComponent sub test: auth test', () => { const fakeAsyncNested = fakeAsyncWithFeature.wrap({ beforeEach: () => fixture.componentInstance.login(), afterEach: () => fixture.componentInstance.logout(); }); it('should show user info', () => { // do some test with fixture with authenticated user. }); }); }); This feature is useful when we want to do some common initial/cleanup when the component has some `async` tasks (especially `setInterval`) running by 3rd party library.
d34964d
to
fe770f6
You can preview fe770f6 at https://pr40611-fe770f6.ngbuilds.io/. |
In the previous commit, `zone.js` provides a new API `fakeAsync.wrap()` to support `hooks` of `fakeAsync()` to allow user to write common `initial`/'cleanup` logic. This commit add this new `wrap()` API to `@angular/core/testing`.
fe770f6
to
0f8d2ac
You can preview 0f8d2ac at https://pr40611-0f8d2ac.ngbuilds.io/. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Close #40387
Enable global
fakeAsync()
feature by introducing two new functions.beginFakeAsync()
andendFakeAsync()
.Motivation
Consider the similar function from
jest.useFakeTimers()
, we can write test cases like this.So with
jest.useFakeTimers()
, all the following tests are automatically using the fake timers, and the developers can write the commoninit
,cleanup
logic inbeforeEach()/afterEach()
.But with
fakeAsync()
, we can not do the same thing for now.For example, we have a Component like this.
We need to call
fixture.destroy()
inside each tests, since each it() useit's own fakeAsync() and we need to clean up the timerId created in that
FakeAsyncZoneSpec. Otherwise the
fakeAsync()
will throwthere are still pending timers
error.Solution
So in this PR, there are two functions are introduced.
beginFakeAsync()
begin fakeAsync globally, so we can use share one FakeAsyncZoneSpec
instance across multiple test cases.
With this feature, we can do some common cleanup in afterEach().
With the
beginFakeAsync()
, we can write case in this way.This feature will be useful when we want to do some global cleanup
in
afterEach()
when the component have someasync
tasks(especially
setInterval
) running by 3rd party library.