|
1 |
| -# Zone Testing |
| 1 | +# Test Runner |
2 | 2 |
|
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. |
5 | 4 |
|
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. |
10 | 6 |
|
11 |
| -## FakeAsyncTestZoneSpec <TBD> |
| 7 | +```javascript |
| 8 | +describe('test', () => { |
| 9 | + // setTimeout(() => {}, 100); // not allowed |
| 10 | +}); |
| 11 | +``` |
12 | 12 |
|
13 |
| -Add `fakeAsync` document later. |
| 13 | +2. Support `fakeAsync` in `test`. |
14 | 14 |
|
15 |
| -## AsyncTestZoneSpec <TBD> |
| 15 | +```javascript |
| 16 | +import 'zone.js`; |
| 17 | +import 'zone.js/dist/zone-testing`; |
16 | 18 |
|
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 | + })); |
18 | 28 |
|
19 |
| -## SyncTestZoneSpec <TBD> |
| 29 | + it ('Promise', fakeAsync(() => { |
| 30 | + let thenRan = false; |
| 31 | + Promise.resolve(null).then((_) => { |
| 32 | + thenRan = true; |
| 33 | + }); |
20 | 34 |
|
21 |
| -Add `sync` document later. |
| 35 | + expect(thenRan).toEqual(false); |
| 36 | + flushMicrotasks(); |
| 37 | + expect(thenRan).toEqual(true); |
| 38 | + })); |
| 39 | +}); |
| 40 | +``` |
22 | 41 |
|
23 |
| -## Unify jasmine/mocha/jest |
| 42 | +Will add more examples later. |
24 | 43 |
|
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