Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 46dab96

Browse files
committed
doc(test): add draft doc of zone-testing
1 parent ad098b0 commit 46dab96

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed

doc/design/TEST.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Zone Testing
2+
3+
`zone.js` has a `zone-testing.js` bundle, which provides a lot of functionalities for testing.
4+
include:
5+
6+
1. FakeAsyncTestZoneSpec: simulate system timer to make `async` test faster and stable.
7+
2. AsyncTestZoneSpec: automatically wait for all async tasks to finish.
8+
3. SyncTestZoneSpec: force all tests to be synchronized.
9+
4. Jasmine/Mocha/Jest supports.
10+
11+
## FakeAsyncTestZoneSpec <TBD>
12+
13+
Add `fakeAsync` document later.
14+
15+
## AsyncTestZoneSpec <TBD>
16+
17+
Add `async` document later.
18+
19+
## SyncTestZoneSpec <TBD>
20+
21+
Add `sync` document later.
22+
23+
## Unify jasmine/mocha/jest
24+
25+
`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).

doc/design/TEST_RUNNER.md

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Test Runner
2+
3+
`zone-testing` monkey-patch `jasmine` and `mocha` runner to provide the following functionalities.
4+
5+
1. All `describe/xdescribe/fdescribe` are guaranteed to run in `SyncTestZone`, so no `async` code are allowed under `describe` directly.
6+
7+
```javascript
8+
describe('test', () => {
9+
// setTimeout(() => {}, 100); // not allowed
10+
});
11+
```
12+
13+
2. Support `fakeAsync` in `test`.
14+
15+
```javascript
16+
import 'zone.js`;
17+
import 'zone.js/dist/zone-testing`;
18+
// TODO: this is too complex to load fakeAsyncTest, should expose as global function.
19+
const {resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync} = (Zone as any)[Zone.__symbol__('fakeAsyncTest')];
20+
21+
// support simulate timer function.
22+
describe('fakeAsync', () => {
23+
it('setTimeout', fakeAsync(() => {
24+
let called = false;
25+
setTimeout(() => {called = true}, 100);
26+
expect(called).toBe(false);
27+
tick(100);
28+
expect(called).toBe(true);
29+
}));
30+
31+
it ('Promise', fakeAsync(() => {
32+
let thenRan = false;
33+
Promise.resolve(null).then((_) => {
34+
thenRan = true;
35+
});
36+
37+
expect(thenRan).toEqual(false);
38+
flushMicrotasks();
39+
expect(thenRan).toEqual(true);
40+
}));
41+
});
42+
```
43+
44+
Will add more examples later.
45+
46+
3. support `asyncTest`.
47+
48+
```javascript
49+
import 'zone.js`;
50+
import 'zone.js/dist/zone-testing`;
51+
// TODO: this is too complex to load asyncTest, should expose as global function.
52+
const asyncTest = (Zone as any)[Zone.__symbol__('asyncTest')];
53+
54+
describe('async', () => {
55+
it('setTimeout and Promise', asyncTest(() => { // don't need to provide doneFn here.
56+
let timeoutCalled = false;
57+
setTimeout(() => {
58+
timeoutCalled = true;
59+
}, 100);
60+
let thenRan = false;
61+
Promise.resolve(null).then((_) => {
62+
thenRan = true;
63+
});
64+
setTimeout(() => {
65+
expect(timeoutCalled).toBe(true);
66+
expect(thenRan).toBe(true);
67+
}, 200);
68+
}));
69+
});
70+
```
71+
72+
Will add more examples later.
73+
74+
4. Date.now/jasmine.clock()/rxjs.Scheduler support in fakeAsync.
75+
76+
```javascript
77+
describe('fakeAsync', () => {
78+
it('setTimeout', fakeAsync(() => {
79+
const start = Date.now();
80+
testZoneSpec.tick(100);
81+
const end = Date.now();
82+
expect(end - start).toBe(100);
83+
}));
84+
});
85+
86+
// NOTE: automatically fall into fakeAsync need to set this flag to true before loading zone-testing
87+
// (window as any).__zone_symbol__fakeAsyncPatchLock = true;
88+
describe('jasmine.clock', () => {
89+
beforeEach(() => {
90+
jasmine.clock().install();
91+
});
92+
93+
afterEach(() => {
94+
jasmine.clock().uninstall();
95+
});
96+
97+
it('should get date diff correctly', () => { // we don't need fakeAsync here.
98+
// automatically run into fake async zone, because jasmine.clock() is installed.
99+
const start = Date.now();
100+
jasmine.clock().tick(100);
101+
const end = Date.now();
102+
expect(end - start).toBe(100);
103+
});
104+
});
105+
106+
// import the following files to patch Date.now of rxjs.Scheduler/rxjs.asap/rxjs.async
107+
import 'zone.js/dist/zone-patch-rxjs-fakeAsync';
108+
109+
describe('fakeAsync', () => {
110+
it('should get date diff correctly', fakeAsync(() => {
111+
let result = null;
112+
const observable = new Observable((subscribe: any) => {
113+
subscribe.next('hello');
114+
});
115+
observable.delay(1000).subscribe(v => {
116+
result = v;
117+
});
118+
expect(result).toBeNull();
119+
testZoneSpec.tick(1000);
120+
expect(result).toBe('hello');
121+
});
122+
});
123+
```
124+
125+
5. Unify `jasmine/mocha/jest` test cases in `Mocha` runner.
126+
127+
You can write `jasmine`, `mocha`, `jest` style test cases when you use `Mocha` runner.
128+
You can use `jasmine spy` inside `mocha` cases, you can also use `jest style expect and mock`.
129+
130+
```javascript
131+
describe('mixed', () => {
132+
// jasmine style beforeAll
133+
beforeAll(() => {});
134+
135+
// mocha style setup
136+
setup(() => {});
137+
138+
it('jasmine style', () => {
139+
expect(true).toBe(true);
140+
});
141+
142+
// mocha specify
143+
specify('mocha style', () => {
144+
foo = {
145+
setBar: function(value: any) {
146+
bar = value;
147+
}
148+
};
149+
150+
spyOn(foo, 'setBar').and.callThrough();
151+
foo.setBar(123);
152+
expect(bar).toEqual(123);
153+
});
154+
155+
test('jest style', () => {
156+
// TODO: will add type definition later.
157+
(expect([1, 2, 3]) as any).toHaveLength(3);
158+
// can handle promise with jest expect.resolves
159+
return (expect(Promise.resolve('lemon')) as any).resolves.toBe('lemon');
160+
});
161+
162+
test('jest mock', () => {
163+
// can support jest.mock
164+
const myMockFn = jest.fn(() => 'default')
165+
.mockImplementationOnce(() => 'first call')
166+
.mockImplementationOnce(() => 'second call');
167+
168+
// 'first call', 'second call', 'default', 'default'
169+
logs.push(myMockFn(), myMockFn(), myMockFn(), myMockFn());
170+
expect(logs).toEqual(['first call', 'second call', 'default', 'default']);
171+
});
172+
});
173+
```
174+
175+
For full examples, you can find in [jasmine](../../test/spec/mocha/jasmine-bridge.spec.ts) and [jest](../../test/spec/jest-bridge.spec.ts)
176+
177+
And here is the mapping about which `jasmine/jest` functionalities are supported inside `Mocha` runner.
178+
179+
1. BDD/TDD interface.
180+
181+
| jasmine | mocha | jest |
182+
| --- | --- | --- | --- |
183+
| beforeAll | before | beforeAll |
184+
| afterAll | after | beforeAll |
185+
| xdescribe | describe.skip | describe.skip |
186+
| fdescribe | describe.only | describe.only |
187+
| xit | it.skip | it.skip |
188+
| fit | it.only | it.only |
189+
190+
And of course you can use `setup/suiteSetup/tearDown/suiteTearDown` in Mocha.
191+
192+
2. jasmine.clock
193+
You can use `jasmine.clock` inside `Mocha` runner. And you can also use the `auto fakeAsync` feature.
194+
195+
3. jasmine.spy
196+
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/...`
197+
198+
4. jest mock
199+
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`.
200+
201+
5. jasmine expect
202+
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`.
203+
204+
- nothing
205+
- toBe
206+
- toBeCloseTo
207+
- toEqual
208+
- toBeGreaterThan
209+
- toBeGreaterThanOrEqual
210+
- toBeLessThan
211+
- toBeLessThanOrEqual
212+
- toBeDefined
213+
- toBeNaN
214+
- toBeNegativeInfinity
215+
- toBeNull
216+
- toBePositiveInfinity
217+
- toBeUndefined
218+
- toThrow
219+
- toThrowError
220+
- toBeTruthy
221+
- toBeFalsy
222+
- toContain
223+
- toHaveBeenCalled
224+
- toHaveBeenCalledWith
225+
- toMatch
226+
- not
227+
228+
You can also add customMatchers and customEqualityTesters.
229+
230+
6. Jest mock
231+
And you can also get `jest expect matchers`.
232+
233+
- toBeCalled
234+
- toBeCalledWith
235+
- toHaveBeenCalledTimes
236+
- lastCalledWith
237+
- toHaveBeenLastCalledWith
238+
- toBeInstanceOf
239+
- toContainEqual
240+
- toHaveLength
241+
- toHaveProperty
242+
- toMatchObject
243+
244+
And `expect util method`.
245+
246+
- expect.anything
247+
- expect.any
248+
- expect.arrayContaining
249+
- expect.objectContaining
250+
- expect.stringContaining
251+
- expect.stringMatching
252+
- expect.extend
253+
- expect.assertions
254+
- expect.hasAssertions
255+
- expect.resolves (Promise)
256+
- expect.rejects (Promise)

0 commit comments

Comments
 (0)