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

Commit d60b1ea

Browse files
committed
doc(test): add more test document
1 parent 26a2b61 commit d60b1ea

File tree

8 files changed

+276
-279
lines changed

8 files changed

+276
-279
lines changed

doc/design/TEST.md

+251-15
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,261 @@
1-
# Zone Testing
1+
# Test Runner
22

3-
`zone.js` has a `zone-testing.js` bundle, which provides a lot of functionalities for testing.
4-
include:
3+
`zone-testing` monkey-patch `jasmine` and `mocha` runner to provide the following functionalities.
54

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.
5+
1. All `describe/xdescribe/fdescribe` are guaranteed to run in `SyncTestZone`, so no `async` code are allowed under `describe` directly.
106

11-
## FakeAsyncTestZoneSpec <TBD>
7+
```javascript
8+
describe('test', () => {
9+
// setTimeout(() => {}, 100); // not allowed
10+
});
11+
```
1212

13-
Add `fakeAsync` document later.
13+
2. Support `fakeAsync` in `test`.
1414

15-
## AsyncTestZoneSpec <TBD>
15+
```javascript
16+
import 'zone.js`;
17+
import 'zone.js/dist/zone-testing`;
1618
17-
Add `async` document later.
19+
// support simulate timer function.
20+
describe('fakeAsync', () => {
21+
it('setTimeout', fakeAsync(() => {
22+
let called = false;
23+
setTimeout(() => {called = true}, 100);
24+
expect(called).toBe(false);
25+
tick(100);
26+
expect(called).toBe(true);
27+
}));
1828
19-
## SyncTestZoneSpec <TBD>
29+
it ('Promise', fakeAsync(() => {
30+
let thenRan = false;
31+
Promise.resolve(null).then((_) => {
32+
thenRan = true;
33+
});
2034
21-
Add `sync` document later.
35+
expect(thenRan).toEqual(false);
36+
flushMicrotasks();
37+
expect(thenRan).toEqual(true);
38+
}));
39+
});
40+
```
2241

23-
## Unify jasmine/mocha/jest
42+
Will add more examples later.
2443

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).
44+
3. support `asyncTest`.
45+
46+
```javascript
47+
import 'zone.js`;
48+
import 'zone.js/dist/zone-testing`;
49+
// TODO: this is too complex to load asyncTest, should expose as global function.
50+
const asyncTest = (Zone as any)[Zone.__symbol__('asyncTest')];
51+
52+
describe('async', () => {
53+
it('setTimeout and Promise', asyncTest(() => { // don't need to provide doneFn here.
54+
let timeoutCalled = false;
55+
setTimeout(() => {
56+
timeoutCalled = true;
57+
}, 100);
58+
let thenRan = false;
59+
Promise.resolve(null).then((_) => {
60+
thenRan = true;
61+
});
62+
setTimeout(() => {
63+
expect(timeoutCalled).toBe(true);
64+
expect(thenRan).toBe(true);
65+
}, 200);
66+
}));
67+
});
68+
```
69+
70+
Will add more examples later.
71+
72+
And `asyncTest/fakeAsyncTest/flush/tick/...` those APIs have been exposed as `global APIs`, you can check this type definition file for the full list. [zone-testing.typing.ts](../../lib/testing/zone-testing.typing.ts).
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 use `jest style expect and mock`, and you can use `jasmine clock` and `jest TimerMock`.
129+
130+
for the full list of supported APIs, please check this type definition file. [zone-testing.typing.ts](../../lib/testing/zone-testing.typing.ts).
131+
132+
```javascript
133+
describe('mixed', () => {
134+
// jasmine style beforeAll
135+
beforeAll(() => {});
136+
137+
// mocha style setup
138+
setup(() => {});
139+
140+
it('jasmine style', () => {
141+
expect(true).toBe(true);
142+
});
143+
144+
// mocha specify
145+
specify('mocha style', () => {
146+
foo = {
147+
setBar: function(value: any) {
148+
bar = value;
149+
}
150+
};
151+
152+
spyOn(foo, 'setBar').and.callThrough();
153+
foo.setBar(123);
154+
expect(bar).toEqual(123);
155+
});
156+
157+
test('jest style', () => {
158+
// TODO: will add type definition later.
159+
(expect([1, 2, 3]) as any).toHaveLength(3);
160+
// can handle promise with jest expect.resolves
161+
return (expect(Promise.resolve('lemon')) as any).resolves.toBe('lemon');
162+
});
163+
164+
test('jest mock', () => {
165+
// can support jest.mock
166+
const myMockFn = jest.fn(() => 'default')
167+
.mockImplementationOnce(() => 'first call')
168+
.mockImplementationOnce(() => 'second call');
169+
170+
// 'first call', 'second call', 'default', 'default'
171+
logs.push(myMockFn(), myMockFn(), myMockFn(), myMockFn());
172+
expect(logs).toEqual(['first call', 'second call', 'default', 'default']);
173+
});
174+
});
175+
```
176+
177+
For full examples, you can find in [jasmine](../../test/spec/mocha/jasmine-bridge.spec.ts) and [jest](../../test/spec/jest-bridge.spec.ts)
178+
179+
And here is the mapping about which `jasmine/jest` functionalities are supported inside `Mocha` runner.
180+
181+
1. BDD/TDD interface.
182+
183+
| jasmine | mocha | jest |
184+
| --- | --- | --- |
185+
| beforeAll | before | beforeAll |
186+
| afterAll | after | beforeAll |
187+
| xdescribe | describe.skip | describe.skip |
188+
| fdescribe | describe.only | describe.only |
189+
| xit | it.skip | it.skip |
190+
| fit | it.only | it.only |
191+
192+
And of course you can use `setup/suiteSetup/tearDown/suiteTearDown` in Mocha.
193+
194+
2. jasmine.clock
195+
You can use `jasmine.clock` inside `Mocha` runner. And you can also use the `auto fakeAsync` feature.
196+
197+
3. jest TimerMock
198+
You can use `jest.useFakeTimers()` to setup jest Timer Mock, and you can use `jest.runAllTimers()/jest.runOnlyPendingTimers()/jest.advanceTimersByTime()/...` to do the time control.
199+
200+
3. jasmine.spy
201+
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/...`
202+
203+
4. jest mock
204+
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`.
205+
206+
5. jasmine expect
207+
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`.
208+
209+
- nothing
210+
- toBe
211+
- toBeCloseTo
212+
- toEqual
213+
- toBeGreaterThan
214+
- toBeGreaterThanOrEqual
215+
- toBeLessThan
216+
- toBeLessThanOrEqual
217+
- toBeDefined
218+
- toBeNaN
219+
- toBeNegativeInfinity
220+
- toBeNull
221+
- toBePositiveInfinity
222+
- toBeUndefined
223+
- toThrow
224+
- toThrowError
225+
- toBeTruthy
226+
- toBeFalsy
227+
- toContain
228+
- toHaveBeenCalled
229+
- toHaveBeenCalledWith
230+
- toMatch
231+
- not
232+
233+
You can also add customMatchers and customEqualityTesters.
234+
235+
6. Jest expect
236+
And you can also get `jest expect matchers`.
237+
238+
- toBeCalled
239+
- toBeCalledWith
240+
- toHaveBeenCalledTimes
241+
- lastCalledWith
242+
- toHaveBeenLastCalledWith
243+
- toBeInstanceOf
244+
- toContainEqual
245+
- toHaveLength
246+
- toHaveProperty
247+
- toMatchObject
248+
249+
And `expect util method`.
250+
251+
- expect.anything
252+
- expect.any
253+
- expect.arrayContaining
254+
- expect.objectContaining
255+
- expect.stringContaining
256+
- expect.stringMatching
257+
- expect.extend
258+
- expect.assertions
259+
- expect.hasAssertions
260+
- expect.resolves (Promise)
261+
- expect.rejects (Promise)

0 commit comments

Comments
 (0)