This repository has been archived by the owner on Feb 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 408
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
doc(test): add draft doc of zone-testing
- Loading branch information
1 parent
ad098b0
commit 46dab96
Showing
2 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Zone Testing | ||
|
||
`zone.js` has a `zone-testing.js` bundle, which provides a lot of functionalities for testing. | ||
include: | ||
|
||
1. FakeAsyncTestZoneSpec: simulate system timer to make `async` test faster and stable. | ||
2. AsyncTestZoneSpec: automatically wait for all async tasks to finish. | ||
3. SyncTestZoneSpec: force all tests to be synchronized. | ||
4. Jasmine/Mocha/Jest supports. | ||
|
||
## FakeAsyncTestZoneSpec <TBD> | ||
|
||
Add `fakeAsync` document later. | ||
|
||
## AsyncTestZoneSpec <TBD> | ||
|
||
Add `async` document later. | ||
|
||
## SyncTestZoneSpec <TBD> | ||
|
||
Add `sync` document later. | ||
|
||
## Unify jasmine/mocha/jest | ||
|
||
`zone-testing` support `jasmine` and `mocha` runner, when `zone-testing` is loaded, it will detect current test environment(jasmine or mocha) and monkey-patch current runner accordingly. For detail, please check this document [test runner](./TEST_RUNNER.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
# Test Runner | ||
|
||
`zone-testing` monkey-patch `jasmine` and `mocha` runner to provide the following functionalities. | ||
|
||
1. All `describe/xdescribe/fdescribe` are guaranteed to run in `SyncTestZone`, so no `async` code are allowed under `describe` directly. | ||
|
||
```javascript | ||
describe('test', () => { | ||
// setTimeout(() => {}, 100); // not allowed | ||
}); | ||
``` | ||
|
||
2. Support `fakeAsync` in `test`. | ||
|
||
```javascript | ||
import 'zone.js`; | ||
import 'zone.js/dist/zone-testing`; | ||
// TODO: this is too complex to load fakeAsyncTest, should expose as global function. | ||
const {resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync} = (Zone as any)[Zone.__symbol__('fakeAsyncTest')]; | ||
// support simulate timer function. | ||
describe('fakeAsync', () => { | ||
it('setTimeout', fakeAsync(() => { | ||
let called = false; | ||
setTimeout(() => {called = true}, 100); | ||
expect(called).toBe(false); | ||
tick(100); | ||
expect(called).toBe(true); | ||
})); | ||
it ('Promise', fakeAsync(() => { | ||
let thenRan = false; | ||
Promise.resolve(null).then((_) => { | ||
thenRan = true; | ||
}); | ||
expect(thenRan).toEqual(false); | ||
flushMicrotasks(); | ||
expect(thenRan).toEqual(true); | ||
})); | ||
}); | ||
``` | ||
|
||
Will add more examples later. | ||
|
||
3. support `asyncTest`. | ||
|
||
```javascript | ||
import 'zone.js`; | ||
import 'zone.js/dist/zone-testing`; | ||
// TODO: this is too complex to load asyncTest, should expose as global function. | ||
const asyncTest = (Zone as any)[Zone.__symbol__('asyncTest')]; | ||
describe('async', () => { | ||
it('setTimeout and Promise', asyncTest(() => { // don't need to provide doneFn here. | ||
let timeoutCalled = false; | ||
setTimeout(() => { | ||
timeoutCalled = true; | ||
}, 100); | ||
let thenRan = false; | ||
Promise.resolve(null).then((_) => { | ||
thenRan = true; | ||
}); | ||
setTimeout(() => { | ||
expect(timeoutCalled).toBe(true); | ||
expect(thenRan).toBe(true); | ||
}, 200); | ||
})); | ||
}); | ||
``` | ||
|
||
Will add more examples later. | ||
|
||
4. Date.now/jasmine.clock()/rxjs.Scheduler support in fakeAsync. | ||
|
||
```javascript | ||
describe('fakeAsync', () => { | ||
it('setTimeout', fakeAsync(() => { | ||
const start = Date.now(); | ||
testZoneSpec.tick(100); | ||
const end = Date.now(); | ||
expect(end - start).toBe(100); | ||
})); | ||
}); | ||
|
||
// NOTE: automatically fall into fakeAsync need to set this flag to true before loading zone-testing | ||
// (window as any).__zone_symbol__fakeAsyncPatchLock = true; | ||
describe('jasmine.clock', () => { | ||
beforeEach(() => { | ||
jasmine.clock().install(); | ||
}); | ||
|
||
afterEach(() => { | ||
jasmine.clock().uninstall(); | ||
}); | ||
|
||
it('should get date diff correctly', () => { // we don't need fakeAsync here. | ||
// automatically run into fake async zone, because jasmine.clock() is installed. | ||
const start = Date.now(); | ||
jasmine.clock().tick(100); | ||
const end = Date.now(); | ||
expect(end - start).toBe(100); | ||
}); | ||
}); | ||
|
||
// import the following files to patch Date.now of rxjs.Scheduler/rxjs.asap/rxjs.async | ||
import 'zone.js/dist/zone-patch-rxjs-fakeAsync'; | ||
|
||
describe('fakeAsync', () => { | ||
it('should get date diff correctly', fakeAsync(() => { | ||
let result = null; | ||
const observable = new Observable((subscribe: any) => { | ||
subscribe.next('hello'); | ||
}); | ||
observable.delay(1000).subscribe(v => { | ||
result = v; | ||
}); | ||
expect(result).toBeNull(); | ||
testZoneSpec.tick(1000); | ||
expect(result).toBe('hello'); | ||
}); | ||
}); | ||
``` | ||
5. Unify `jasmine/mocha/jest` test cases in `Mocha` runner. | ||
You can write `jasmine`, `mocha`, `jest` style test cases when you use `Mocha` runner. | ||
You can use `jasmine spy` inside `mocha` cases, you can also use `jest style expect and mock`. | ||
```javascript | ||
describe('mixed', () => { | ||
// jasmine style beforeAll | ||
beforeAll(() => {}); | ||
|
||
// mocha style setup | ||
setup(() => {}); | ||
|
||
it('jasmine style', () => { | ||
expect(true).toBe(true); | ||
}); | ||
|
||
// mocha specify | ||
specify('mocha style', () => { | ||
foo = { | ||
setBar: function(value: any) { | ||
bar = value; | ||
} | ||
}; | ||
|
||
spyOn(foo, 'setBar').and.callThrough(); | ||
foo.setBar(123); | ||
expect(bar).toEqual(123); | ||
}); | ||
|
||
test('jest style', () => { | ||
// TODO: will add type definition later. | ||
(expect([1, 2, 3]) as any).toHaveLength(3); | ||
// can handle promise with jest expect.resolves | ||
return (expect(Promise.resolve('lemon')) as any).resolves.toBe('lemon'); | ||
}); | ||
|
||
test('jest mock', () => { | ||
// can support jest.mock | ||
const myMockFn = jest.fn(() => 'default') | ||
.mockImplementationOnce(() => 'first call') | ||
.mockImplementationOnce(() => 'second call'); | ||
|
||
// 'first call', 'second call', 'default', 'default' | ||
logs.push(myMockFn(), myMockFn(), myMockFn(), myMockFn()); | ||
expect(logs).toEqual(['first call', 'second call', 'default', 'default']); | ||
}); | ||
}); | ||
``` | ||
For full examples, you can find in [jasmine](../../test/spec/mocha/jasmine-bridge.spec.ts) and [jest](../../test/spec/jest-bridge.spec.ts) | ||
And here is the mapping about which `jasmine/jest` functionalities are supported inside `Mocha` runner. | ||
1. BDD/TDD interface. | ||
| jasmine | mocha | jest | | ||
| --- | --- | --- | --- | | ||
| beforeAll | before | beforeAll | | ||
| afterAll | after | beforeAll | | ||
| xdescribe | describe.skip | describe.skip | | ||
| fdescribe | describe.only | describe.only | | ||
| xit | it.skip | it.skip | | ||
| fit | it.only | it.only | | ||
And of course you can use `setup/suiteSetup/tearDown/suiteTearDown` in Mocha. | ||
2. jasmine.clock | ||
You can use `jasmine.clock` inside `Mocha` runner. And you can also use the `auto fakeAsync` feature. | ||
3. jasmine.spy | ||
In Mocha, no built-in spy lib is provided, you can still use 3rd party spy library, with `zone-testing`, you can use `jasmine spy`, you can use `jasmine.createSpy, jasmine.createSpyObj, spyOn, spyOnProperty` to create `jasmine style spy`. And you can also use all `spy strategy` such as `callThough/callFake/...` | ||
4. jest mock | ||
Not only `jasmine spy`, you can also use `jest mock`, You can use `jest.fn` to create `jest style mock`. And you can also use the `jest mockFn method` such as `mochFn.mockReturnValue`. | ||
5. jasmine expect | ||
In Mocha, there is no built in `expect` lib, you can still use 3rd party `assert/expect` lib, with `zone-testing`, you can use `jasmine expect mathers`. | ||
- nothing | ||
- toBe | ||
- toBeCloseTo | ||
- toEqual | ||
- toBeGreaterThan | ||
- toBeGreaterThanOrEqual | ||
- toBeLessThan | ||
- toBeLessThanOrEqual | ||
- toBeDefined | ||
- toBeNaN | ||
- toBeNegativeInfinity | ||
- toBeNull | ||
- toBePositiveInfinity | ||
- toBeUndefined | ||
- toThrow | ||
- toThrowError | ||
- toBeTruthy | ||
- toBeFalsy | ||
- toContain | ||
- toHaveBeenCalled | ||
- toHaveBeenCalledWith | ||
- toMatch | ||
- not | ||
You can also add customMatchers and customEqualityTesters. | ||
6. Jest mock | ||
And you can also get `jest expect matchers`. | ||
- toBeCalled | ||
- toBeCalledWith | ||
- toHaveBeenCalledTimes | ||
- lastCalledWith | ||
- toHaveBeenLastCalledWith | ||
- toBeInstanceOf | ||
- toContainEqual | ||
- toHaveLength | ||
- toHaveProperty | ||
- toMatchObject | ||
And `expect util method`. | ||
- expect.anything | ||
- expect.any | ||
- expect.arrayContaining | ||
- expect.objectContaining | ||
- expect.stringContaining | ||
- expect.stringMatching | ||
- expect.extend | ||
- expect.assertions | ||
- expect.hasAssertions | ||
- expect.resolves (Promise) | ||
- expect.rejects (Promise) |