Skip to content

Commit e748add

Browse files
committed
refactor(testing): move common testing logic into test_injector
Before, all test framework wrappers (internal for dart and js/ts, angular2_test for dart and testing for js/ts) had similar logic to keep track of current global test injector and test provider list. This change wraps that logic into one class managed by the test injector. Closes angular#5920
1 parent 630d931 commit e748add

13 files changed

Lines changed: 119 additions & 93 deletions

modules/angular2/src/testing/test_injector.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
defaultKeyValueDiffers,
2424
ChangeDetectorGenConfig
2525
} from 'angular2/src/core/change_detection/change_detection';
26-
import {ExceptionHandler} from 'angular2/src/facade/exceptions';
26+
import {BaseException, ExceptionHandler} from 'angular2/src/facade/exceptions';
2727
import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
2828
import {XHR} from 'angular2/src/compiler/xhr';
2929

@@ -131,11 +131,62 @@ function _runtimeCompilerBindings() {
131131
];
132132
}
133133

134+
export class TestInjector {
135+
private _instantiated: boolean = false;
136+
137+
private _injector: Injector = null;
138+
139+
private _providers: Array<Type | Provider | any[]> = [];
140+
141+
reset() {
142+
this._injector = null;
143+
this._providers = [];
144+
this._instantiated = false;
145+
}
146+
147+
addProviders(providers: Array<Type | Provider | any[]>) {
148+
if (this._instantiated) {
149+
throw new BaseException('Cannot add providers after test injector is instantiated');
150+
}
151+
this._providers = ListWrapper.concat(this._providers, providers);
152+
}
153+
154+
createInjector() {
155+
var rootInjector = Injector.resolveAndCreate(_getRootProviders());
156+
this._injector = rootInjector.resolveAndCreateChild(ListWrapper.concat(
157+
ListWrapper.concat(_getAppBindings(), _runtimeCompilerBindings()), this._providers));
158+
this._instantiated = true;
159+
return this._injector;
160+
}
161+
162+
execute(fn: FunctionWithParamTokens): any {
163+
if (!this._instantiated) {
164+
this.createInjector();
165+
}
166+
return fn.execute(this._injector);
167+
}
168+
}
169+
170+
var _testInjector: TestInjector = null;
171+
172+
export function getTestInjector() {
173+
if (_testInjector == null) {
174+
_testInjector = new TestInjector();
175+
}
176+
return _testInjector;
177+
}
178+
179+
/**
180+
* @deprecated Use TestInjector#createInjector() instead.
181+
*/
134182
export function createTestInjector(providers: Array<Type | Provider | any[]>): Injector {
135183
var rootInjector = Injector.resolveAndCreate(_getRootProviders());
136184
return rootInjector.resolveAndCreateChild(ListWrapper.concat(_getAppBindings(), providers));
137185
}
138186

187+
/**
188+
* @deprecated Use TestInjector#createInjector() instead.
189+
*/
139190
export function createTestInjectorWithRuntimeCompiler(
140191
providers: Array<Type | Provider | any[]>): Injector {
141192
return createTestInjector(ListWrapper.concat(_runtimeCompilerBindings(), providers));
@@ -159,7 +210,8 @@ export function createTestInjectorWithRuntimeCompiler(
159210
* ```
160211
*
161212
* Notes:
162-
* - inject is currently a function because of some Traceur limitation the syntax should eventually
213+
* - inject is currently a function because of some Traceur limitation the syntax should
214+
* eventually
163215
* becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });`
164216
*
165217
* @param {Array} tokens

modules/angular2/src/testing/testing.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import {ListWrapper} from 'angular2/src/facade/collection';
77
import {bind} from 'angular2/core';
88

99
import {
10-
createTestInjectorWithRuntimeCompiler,
1110
FunctionWithParamTokens,
1211
inject,
13-
injectAsync
12+
injectAsync,
13+
TestInjector,
14+
getTestInjector
1415
} from './test_injector';
1516

1617
export {inject, injectAsync} from './test_injector';
@@ -92,14 +93,10 @@ var jsmIt = _global.it;
9293
var jsmIIt = _global.fit;
9394
var jsmXIt = _global.xit;
9495

95-
var testProviders;
96-
var injector;
96+
var testInjector: TestInjector = getTestInjector();
9797

9898
// Reset the test providers before each test.
99-
jsmBeforeEach(() => {
100-
testProviders = [];
101-
injector = null;
102-
});
99+
jsmBeforeEach(() => { testInjector.reset(); });
103100

104101
/**
105102
* Allows overriding default providers of the test injector,
@@ -115,8 +112,9 @@ export function beforeEachProviders(fn): void {
115112
jsmBeforeEach(() => {
116113
var providers = fn();
117114
if (!providers) return;
118-
testProviders = [...testProviders, ...providers];
119-
if (injector !== null) {
115+
try {
116+
testInjector.addProviders(providers);
117+
} catch (e) {
120118
throw new Error('beforeEachProviders was called after the injector had ' +
121119
'been used in a beforeEach or it block. This invalidates the ' +
122120
'test injector');
@@ -188,17 +186,13 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
188186

189187
if (testFn instanceof FunctionWithParamTokens) {
190188
jsmFn(name, (done) => {
191-
if (!injector) {
192-
injector = createTestInjectorWithRuntimeCompiler(testProviders);
193-
}
194-
195189
var finishCallback = () => {
196190
// Wait one more event loop to make sure we catch unreturned promises and
197191
// promise rejections.
198192
setTimeout(done, 0);
199193
};
200194
var returnedTestValue =
201-
runInTestZone(() => testFn.execute(injector), finishCallback, done.fail);
195+
runInTestZone(() => testInjector.execute(testFn), finishCallback, done.fail);
202196

203197
if (testFn.isAsync) {
204198
if (_isPromiseLike(returnedTestValue)) {
@@ -243,11 +237,9 @@ export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
243237
// promise rejections.
244238
setTimeout(done, 0);
245239
};
246-
if (!injector) {
247-
injector = createTestInjectorWithRuntimeCompiler(testProviders);
248-
}
249240

250-
var returnedTestValue = runInTestZone(() => fn.execute(injector), finishCallback, done.fail);
241+
var returnedTestValue =
242+
runInTestZone(() => testInjector.execute(fn), finishCallback, done.fail);
251243
if (fn.isAsync) {
252244
if (_isPromiseLike(returnedTestValue)) {
253245
(<Promise<any>>returnedTestValue).then(null, (err) => { done.fail(err); });

modules/angular2/src/testing/testing_internal.dart

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,23 @@ import 'package:angular2/src/core/reflection/reflection.dart';
2121
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
2222

2323
import 'package:angular2/src/core/di/provider.dart' show bind;
24-
import 'package:angular2/src/core/di/injector.dart' show Injector;
2524
import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
2625

2726
import 'test_injector.dart';
2827
export 'test_injector.dart' show inject;
2928

30-
List _testBindings = [];
31-
Injector _injector;
29+
TestInjector _testInjector = getTestInjector();
3230
bool _isCurrentTestAsync;
31+
Future _currentTestFuture;
3332
bool _inIt = false;
3433

3534
class AsyncTestCompleter {
3635
final _completer = new Completer();
3736

37+
AsyncTestCompleter() {
38+
_currentTestFuture = this.future;
39+
}
40+
3841
void done() {
3942
_completer.complete();
4043
}
@@ -50,10 +53,11 @@ void testSetup() {
5053
// - Priority 1: create the test injector to be used in beforeEach() and it()
5154

5255
gns.beforeEach(() {
53-
_testBindings.clear();
56+
_testInjector.reset();
57+
_currentTestFuture = null;
5458
}, priority: 3);
5559

56-
var completerBinding = bind(AsyncTestCompleter).toFactory(() {
60+
var completerProvider = bind(AsyncTestCompleter).toFactory(() {
5761
// Mark the test as async when an AsyncTestCompleter is injected in an it(),
5862
if (!_inIt) throw 'AsyncTestCompleter can only be injected in an "it()"';
5963
_isCurrentTestAsync = true;
@@ -62,15 +66,14 @@ void testSetup() {
6266

6367
gns.beforeEach(() {
6468
_isCurrentTestAsync = false;
65-
_testBindings.add(completerBinding);
66-
_injector = createTestInjectorWithRuntimeCompiler(_testBindings);
69+
_testInjector.addProviders([completerProvider]);
6770
}, priority: 1);
6871
}
6972

7073
/**
71-
* Allows overriding default bindings defined in test_injector.js.
74+
* Allows overriding default providers defined in test_injector.js.
7275
*
73-
* The given function must return a list of DI bindings.
76+
* The given function must return a list of DI providers.
7477
*
7578
* Example:
7679
*
@@ -81,8 +84,8 @@ void testSetup() {
8184
*/
8285
void beforeEachProviders(Function fn) {
8386
gns.beforeEach(() {
84-
var bindings = fn();
85-
if (bindings != null) _testBindings.addAll(bindings);
87+
var providers = fn();
88+
if (providers != null) _testInjector.addProviders(providers);
8689
}, priority: 2);
8790
}
8891

@@ -95,7 +98,7 @@ void beforeEach(fn) {
9598
if (fn is! FunctionWithParamTokens) fn =
9699
new FunctionWithParamTokens([], fn, false);
97100
gns.beforeEach(() {
98-
fn.execute(_injector);
101+
_testInjector.execute(fn);
99102
});
100103
}
101104

@@ -104,9 +107,9 @@ void _it(gnsFn, name, fn) {
104107
new FunctionWithParamTokens([], fn, false);
105108
gnsFn(name, () {
106109
_inIt = true;
107-
fn.execute(_injector);
110+
_testInjector.execute(fn);
108111
_inIt = false;
109-
if (_isCurrentTestAsync) return _injector.get(AsyncTestCompleter).future;
112+
if (_isCurrentTestAsync) return _currentTestFuture;
110113
});
111114
}
112115

modules/angular2/src/testing/testing_internal.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import {NgZoneZone} from 'angular2/src/core/zone/ng_zone';
55

66
import {provide} from 'angular2/core';
77

8-
import {
9-
createTestInjectorWithRuntimeCompiler,
10-
FunctionWithParamTokens,
11-
inject
12-
} from './test_injector';
8+
import {TestInjector, getTestInjector, FunctionWithParamTokens, inject} from './test_injector';
139
import {browserDetection} from './utils';
1410

1511
export {inject} from './test_injector';
@@ -48,7 +44,7 @@ var inIt = false;
4844
jasmine.DEFAULT_TIMEOUT_INTERVAL = 500;
4945
var globalTimeOut = browserDetection.isSlow ? 3000 : jasmine.DEFAULT_TIMEOUT_INTERVAL;
5046

51-
var testProviders;
47+
var testInjector = getTestInjector();
5248

5349
/**
5450
* Mechanism to run `beforeEach()` functions of Angular tests.
@@ -62,16 +58,17 @@ class BeforeEachRunner {
6258

6359
beforeEach(fn: FunctionWithParamTokens | SyncTestFn): void { this._fns.push(fn); }
6460

65-
run(injector): void {
66-
if (this._parent) this._parent.run(injector);
61+
run(): void {
62+
if (this._parent) this._parent.run();
6763
this._fns.forEach((fn) => {
68-
return isFunction(fn) ? (<SyncTestFn>fn)() : (<FunctionWithParamTokens>fn).execute(injector);
64+
return isFunction(fn) ? (<SyncTestFn>fn)() :
65+
(testInjector.execute(<FunctionWithParamTokens>fn));
6966
});
7067
}
7168
}
7269

7370
// Reset the test providers before each test
74-
jsmBeforeEach(() => { testProviders = []; });
71+
jsmBeforeEach(() => { testInjector.reset(); });
7572

7673
function _describe(jsmFn, ...args) {
7774
var parentRunner = runnerStack.length === 0 ? null : runnerStack[runnerStack.length - 1];
@@ -120,7 +117,7 @@ export function beforeEachProviders(fn): void {
120117
jsmBeforeEach(() => {
121118
var providers = fn();
122119
if (!providers) return;
123-
testProviders = [...testProviders, ...providers];
120+
testInjector.addProviders(providers);
124121
});
125122
}
126123

@@ -150,18 +147,17 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
150147
}
151148
});
152149

153-
var injector = createTestInjectorWithRuntimeCompiler([...testProviders, completerProvider]);
154-
runner.run(injector);
150+
testInjector.addProviders([completerProvider]);
151+
runner.run();
155152

156153
inIt = true;
157-
testFn.execute(injector);
154+
testInjector.execute(testFn);
158155
inIt = false;
159156
}, timeOut);
160157
} else {
161158
jsmFn(name, () => {
162-
var injector = createTestInjectorWithRuntimeCompiler(testProviders);
163-
runner.run(injector);
164-
testFn.execute(injector);
159+
runner.run();
160+
testInjector.execute(testFn);
165161
}, timeOut);
166162
}
167163

@@ -170,14 +166,12 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
170166

171167
if ((<any>testFn).length === 0) {
172168
jsmFn(name, () => {
173-
var injector = createTestInjectorWithRuntimeCompiler(testProviders);
174-
runner.run(injector);
169+
runner.run();
175170
(<SyncTestFn>testFn)();
176171
}, timeOut);
177172
} else {
178173
jsmFn(name, (done) => {
179-
var injector = createTestInjectorWithRuntimeCompiler(testProviders);
180-
runner.run(injector);
174+
runner.run();
181175
(<AsyncTestFn>testFn)(done);
182176
}, timeOut);
183177
}

modules/angular2/test/web_workers/debug_tools/multi_client_server_message_bus.server.spec.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import "package:angular2/testing_internal.dart"
1111
iit,
1212
expect,
1313
beforeEach,
14-
createTestInjector,
1514
beforeEachProviders,
1615
SpyObject,
1716
proxy;

modules/angular2/test/web_workers/debug_tools/single_client_server_message_bus.server.spec.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import "package:angular2/testing_internal.dart"
1010
it,
1111
expect,
1212
beforeEach,
13-
createTestInjector,
1413
beforeEachProviders,
1514
SpyObject,
1615
proxy;

modules/angular2/test/web_workers/debug_tools/web_socket_message_bus_spec.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import "package:angular2/testing_internal.dart"
88
it,
99
expect,
1010
beforeEach,
11-
createTestInjector,
1211
beforeEachProviders,
1312
SpyObject,
1413
proxy;

modules/angular2/test/web_workers/shared/message_bus_spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
it,
66
expect,
77
beforeEach,
8-
createTestInjectorWithRuntimeCompiler,
98
beforeEachProviders,
109
SpyObject,
1110
proxy

modules/angular2/test/web_workers/shared/service_message_broker_spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
it,
66
expect,
77
beforeEach,
8-
createTestInjectorWithRuntimeCompiler,
98
beforeEachProviders,
109
SpyObject,
1110
proxy

modules/angular2/test/web_workers/worker/event_dispatcher_spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
it,
66
expect,
77
beforeEach,
8-
createTestInjectorWithRuntimeCompiler,
98
beforeEachProviders,
109
SpyObject,
1110
proxy

0 commit comments

Comments
 (0)