Skip to content

Commit 4979e10

Browse files
committed
add support for setUpClass and tearDownClass
if a test class defines `setUpClass` it will be run before any tests are ever run. if it is asynchronous then no test runs until the future is completed `tearDownClass` is called once after all tests are run the same `InstanceMirror` is used for the setup and for the tear-down, following how setup and tear-down is done for each unit test tests are updated to support and check for new behavior
1 parent 4254267 commit 4979e10

File tree

3 files changed

+51
-12
lines changed

3 files changed

+51
-12
lines changed

pkgs/test_reflective_loader/lib/test_reflective_loader.dart

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ void defineReflectiveTests(Type type) {
8787
{
8888
var isSolo = _hasAnnotationInstance(classMirror, soloTest);
8989
var className = MirrorSystem.getName(classMirror.simpleName);
90-
group = _Group(isSolo, _combineNames(_currentSuiteName, className),
91-
classMirror.testLocation);
90+
group = _Group(
91+
isSolo, _combineNames(_currentSuiteName, className), classMirror);
9292
_currentGroups.add(group);
9393
}
9494

@@ -154,12 +154,16 @@ void _addTestsIfTopLevelSuite() {
154154
if (allGroups || group.isSolo) {
155155
for (var test in group.tests) {
156156
if (allTests || test.isSolo) {
157-
test_package.test(test.name, test.function,
157+
test_package.test(test.name, () async {
158+
await group.ensureSetUpClass();
159+
test.function();
160+
},
158161
timeout: test.timeout,
159162
skip: test.isSkipped,
160163
location: test.location);
161164
}
162165
}
166+
test_package.tearDownAll(() => group.tearDownClass());
163167
}
164168
}
165169
}
@@ -217,7 +221,7 @@ Future<Object?> _invokeSymbolIfExists(
217221
closure = instanceMirror.getField(symbol);
218222
// ignore: avoid_catching_errors
219223
} on NoSuchMethodError {
220-
// ignore
224+
// ignore: empty_catches
221225
}
222226

223227
if (closure is ClosureMirror) {
@@ -307,10 +311,14 @@ class _AssertFailingTest {
307311
class _Group {
308312
final bool isSolo;
309313
final String name;
310-
final test_package.TestLocation? location;
311314
final List<_Test> tests = <_Test>[];
312315

313-
_Group(this.isSolo, this.name, this.location);
316+
/// Shared across group-wide operations eg `setUpClass` and `tearDownClass`.
317+
final InstanceMirror _instance;
318+
Future<Object?>? _setUpCompletion;
319+
320+
_Group(this.isSolo, this.name, ClassMirror classMirror)
321+
: _instance = classMirror.newInstance(const Symbol(''), []);
314322

315323
bool get hasSoloTest => tests.any((test) => test.isSolo);
316324

@@ -327,6 +335,16 @@ class _Group {
327335
tests.add(_Test(isSolo, fullName, function, timeout?._timeout,
328336
memberMirror.testLocation));
329337
}
338+
339+
/// Runs group-wide setup if it has not been started yet,
340+
/// ensuring it only runs once for a group. Set up runs and
341+
/// completes before any test of the group runs
342+
Future<Object?> ensureSetUpClass() =>
343+
_setUpCompletion ??= _invokeSymbolIfExists(_instance, #setUpClass);
344+
345+
/// Runs group-wide tear down after all tests of the group have
346+
/// run
347+
void tearDownClass() => _invokeSymbolIfExists(_instance, #tearDownClass);
330348
}
331349

332350
/// A marker annotation used to instruct dart2js to keep reflection information

pkgs/test_reflective_loader/test/location_test.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ void main() {
4040
// the source code to ensure the locations match up.
4141
name = name.split('|').last.trim();
4242

43+
// Skip "tearDownAll", it never has a location.
44+
if (name == '(tearDownAll)') {
45+
continue;
46+
}
47+
4348
// Expect locations for all remaining fields.
4449
var url = test['url'] as String;
4550
var line = test['line'] as int;

pkgs/test_reflective_loader/test/test_reflective_loader_test.dart

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,31 @@ void main() {
1515
});
1616
}
1717

18+
var _didSetUpClass = false;
19+
var _didTearDownClass = false;
20+
1821
@reflectiveTest
1922
class TestReflectiveLoaderTest {
20-
void test_passes() {
21-
expect(true, true);
23+
void setUpClass() {
24+
_didSetUpClass = true;
25+
}
26+
27+
void tearDownClass() {
28+
_didTearDownClass = true;
29+
}
30+
31+
void test_classwide_state() {
32+
expect(_didSetUpClass, true);
33+
expect(_didTearDownClass, false);
2234
}
2335

2436
@failingTest
2537
void test_fails() {
2638
expect(false, true);
2739
}
2840

29-
@failingTest
30-
void test_fails_throws_sync() {
41+
@skippedTest
42+
void test_fails_but_skipped() {
3143
throw StateError('foo');
3244
}
3345

@@ -36,11 +48,15 @@ class TestReflectiveLoaderTest {
3648
return Future.error('foo');
3749
}
3850

39-
@skippedTest
40-
void test_fails_but_skipped() {
51+
@failingTest
52+
void test_fails_throws_sync() {
4153
throw StateError('foo');
4254
}
4355

56+
void test_passes() {
57+
expect(true, true);
58+
}
59+
4460
@skippedTest
4561
void test_times_out_but_skipped() {
4662
while (true) {}

0 commit comments

Comments
 (0)