Skip to content

Commit 71317eb

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 tests are updated to support and check for new behavior
1 parent 4254267 commit 71317eb

File tree

3 files changed

+99
-14
lines changed

3 files changed

+99
-14
lines changed

pkgs/test_reflective_loader/lib/test_reflective_loader.dart

Lines changed: 36 additions & 8 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

@@ -151,10 +151,22 @@ void _addTestsIfTopLevelSuite() {
151151
if (_currentSuiteLevel == 0) {
152152
void runTests({required bool allGroups, required bool allTests}) {
153153
for (var group in _currentGroups) {
154+
var runningTestCount = 0;
154155
if (allGroups || group.isSolo) {
155156
for (var test in group.tests) {
156157
if (allTests || test.isSolo) {
157-
test_package.test(test.name, test.function,
158+
test_package.test(test.name, () async {
159+
runningTestCount += 1;
160+
await group.ensureSetUpClass();
161+
try {
162+
await test.function();
163+
} finally {
164+
runningTestCount -= 1;
165+
if (runningTestCount == 0) {
166+
group.tearDownClass();
167+
}
168+
}
169+
},
158170
timeout: test.timeout,
159171
skip: test.isSkipped,
160172
location: test.location);
@@ -210,14 +222,14 @@ bool _hasSkippedTestAnnotation(MethodMirror method) =>
210222
_hasAnnotationInstance(method, skippedTest);
211223

212224
Future<Object?> _invokeSymbolIfExists(
213-
InstanceMirror instanceMirror, Symbol symbol) {
225+
ObjectMirror objectMirror, Symbol symbol) {
214226
Object? invocationResult;
215227
InstanceMirror? closure;
216228
try {
217-
closure = instanceMirror.getField(symbol);
229+
closure = objectMirror.getField(symbol);
218230
// ignore: avoid_catching_errors
219231
} on NoSuchMethodError {
220-
// ignore
232+
// ignore: empty_catches
221233
}
222234

223235
if (closure is ClosureMirror) {
@@ -307,10 +319,13 @@ class _AssertFailingTest {
307319
class _Group {
308320
final bool isSolo;
309321
final String name;
310-
final test_package.TestLocation? location;
311322
final List<_Test> tests = <_Test>[];
312323

313-
_Group(this.isSolo, this.name, this.location);
324+
/// For static group-wide operations eg `setUpClass` and `tearDownClass`.
325+
final ClassMirror _classMirror;
326+
Future<Object?>? _setUpCompletion;
327+
328+
_Group(this.isSolo, this.name, this._classMirror);
314329

315330
bool get hasSoloTest => tests.any((test) => test.isSolo);
316331

@@ -327,6 +342,19 @@ class _Group {
327342
tests.add(_Test(isSolo, fullName, function, timeout?._timeout,
328343
memberMirror.testLocation));
329344
}
345+
346+
/// Runs group-wide setup if it has not been started yet,
347+
/// ensuring it only runs once for a group. Set up runs and
348+
/// completes before any test of the group runs
349+
Future<Object?> ensureSetUpClass() =>
350+
_setUpCompletion ??= _invokeSymbolIfExists(_classMirror, #setUpClass);
351+
352+
/// Runs group-wide tear down iff [ensureSetUpClass] was called at least once.
353+
/// Must be called once and only called after all tests of the group have
354+
/// completed
355+
void tearDownClass() => _setUpCompletion != null
356+
? _invokeSymbolIfExists(_classMirror, #tearDownClass)
357+
: null;
330358
}
331359

332360
/// 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" or "setUpAll", it never has a location.
44+
if (name case '(tearDownAll)' || '(setUpAll)') {
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: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,50 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
1111

1212
void main() {
1313
defineReflectiveSuite(() {
14+
setUpAll(() {
15+
expect(TestReflectiveLoaderTest.didSetUpClass, false);
16+
expect(TestReflectiveLoaderTest.didTearDownClass, false);
17+
expect(SecondTest.didSetUpClass, false);
18+
expect(SecondTest.didTearDownClass, false);
19+
});
1420
defineReflectiveTests(TestReflectiveLoaderTest);
21+
defineReflectiveTests(SecondTest);
22+
tearDownAll(() {
23+
expect(TestReflectiveLoaderTest.didSetUpClass, true);
24+
expect(TestReflectiveLoaderTest.didTearDownClass, true);
25+
expect(SecondTest.didSetUpClass, true);
26+
expect(SecondTest.didTearDownClass, true);
27+
});
1528
});
1629
}
1730

1831
@reflectiveTest
1932
class TestReflectiveLoaderTest {
20-
void test_passes() {
21-
expect(true, true);
33+
static bool didSetUpClass = false;
34+
static bool didTearDownClass = false;
35+
36+
static void setUpClass() {
37+
didSetUpClass = true;
38+
expect(didTearDownClass, false);
39+
}
40+
41+
static void tearDownClass() {
42+
expect(didSetUpClass, true);
43+
didTearDownClass = true;
44+
}
45+
46+
void test_classwide_state() {
47+
expect(didSetUpClass, true);
48+
expect(didTearDownClass, false);
2249
}
2350

2451
@failingTest
2552
void test_fails() {
2653
expect(false, true);
2754
}
2855

29-
@failingTest
30-
void test_fails_throws_sync() {
56+
@skippedTest
57+
void test_fails_but_skipped() {
3158
throw StateError('foo');
3259
}
3360

@@ -36,13 +63,38 @@ class TestReflectiveLoaderTest {
3663
return Future.error('foo');
3764
}
3865

39-
@skippedTest
40-
void test_fails_but_skipped() {
66+
@failingTest
67+
void test_fails_throws_sync() {
4168
throw StateError('foo');
4269
}
4370

71+
void test_passes() {
72+
expect(true, true);
73+
}
74+
4475
@skippedTest
4576
void test_times_out_but_skipped() {
4677
while (true) {}
4778
}
4879
}
80+
81+
@reflectiveTest
82+
class SecondTest {
83+
static bool didSetUpClass = false;
84+
static bool didTearDownClass = false;
85+
86+
static void setUpClass() {
87+
didSetUpClass = true;
88+
expect(didTearDownClass, false);
89+
}
90+
91+
static void tearDownClass() {
92+
expect(didSetUpClass, true);
93+
didTearDownClass = true;
94+
}
95+
96+
void test_classwide_state() {
97+
expect(didSetUpClass, true);
98+
expect(didTearDownClass, false);
99+
}
100+
}

0 commit comments

Comments
 (0)