Skip to content

Commit 6c1eb21

Browse files
authored
add support for setUpClass and tearDownClass (#2164)
1 parent 042b03e commit 6c1eb21

File tree

5 files changed

+127
-15
lines changed

5 files changed

+127
-15
lines changed

pkgs/test_reflective_loader/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.4.0
2+
3+
- Add support for one-time set up and teardown in test classes via static
4+
`setUpClass` and `tearDownClass` methods
5+
16
## 0.3.0
27

38
- Require Dart `^3.5.0`.

pkgs/test_reflective_loader/lib/test_reflective_loader.dart

Lines changed: 38 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,24 @@ void _addTestsIfTopLevelSuite() {
151151
if (_currentSuiteLevel == 0) {
152152
void runTests({required bool allGroups, required bool allTests}) {
153153
for (var group in _currentGroups) {
154+
var runTestCount = 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+
if (!test.isSkipped) {
159+
runTestCount += 1;
160+
}
161+
test_package.test(test.name, () async {
162+
await group.ensureSetUpClass();
163+
try {
164+
await test.function();
165+
} finally {
166+
runTestCount -= 1;
167+
if (runTestCount == 0) {
168+
group.tearDownClass();
169+
}
170+
}
171+
},
158172
timeout: test.timeout,
159173
skip: test.isSkipped,
160174
location: test.location);
@@ -210,14 +224,14 @@ bool _hasSkippedTestAnnotation(MethodMirror method) =>
210224
_hasAnnotationInstance(method, skippedTest);
211225

212226
Future<Object?> _invokeSymbolIfExists(
213-
InstanceMirror instanceMirror, Symbol symbol) {
227+
ObjectMirror objectMirror, Symbol symbol) {
214228
Object? invocationResult;
215229
InstanceMirror? closure;
216230
try {
217-
closure = instanceMirror.getField(symbol);
231+
closure = objectMirror.getField(symbol);
218232
// ignore: avoid_catching_errors
219233
} on NoSuchMethodError {
220-
// ignore
234+
// ignore: empty_catches
221235
}
222236

223237
if (closure is ClosureMirror) {
@@ -307,10 +321,13 @@ class _AssertFailingTest {
307321
class _Group {
308322
final bool isSolo;
309323
final String name;
310-
final test_package.TestLocation? location;
311324
final List<_Test> tests = <_Test>[];
312325

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

315332
bool get hasSoloTest => tests.any((test) => test.isSolo);
316333

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

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

pkgs/test_reflective_loader/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: test_reflective_loader
2-
version: 0.3.0
2+
version: 0.4.0
33
description: Support for discovering tests and test suites using reflection.
44
repository: https://github.com/dart-lang/tools/tree/main/pkgs/test_reflective_loader
55
issue_tracker: https://github.com/dart-lang/tools/labels/package%3Atest_reflective_loader

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: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,62 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
1111

1212
void main() {
1313
defineReflectiveSuite(() {
14+
// ensure set-ups and tear-downs are not prematurely called ie before any
15+
// tests actually execute
16+
setUpAll(() {
17+
expect(TestReflectiveLoaderTest.didSetUpClass, false);
18+
expect(TestReflectiveLoaderTest.didTearDownClass, false);
19+
expect(SecondTest.didSetUpClass, false);
20+
expect(SecondTest.didTearDownClass, false);
21+
});
1422
defineReflectiveTests(TestReflectiveLoaderTest);
23+
defineReflectiveTests(SecondTest);
24+
tearDownAll(() {
25+
expect(TestReflectiveLoaderTest.didSetUpClass, true);
26+
expect(TestReflectiveLoaderTest.didTearDownClass, true);
27+
expect(SecondTest.didSetUpClass, true);
28+
expect(SecondTest.didTearDownClass, true);
29+
});
1530
});
1631
}
1732

1833
@reflectiveTest
1934
class TestReflectiveLoaderTest {
20-
void test_passes() {
21-
expect(true, true);
35+
static bool didSetUpClass = false;
36+
static bool didTearDownClass = false;
37+
38+
// TODO(scheglov): Linter was updated to automatically ignore
39+
// this but needs time before it is actually used. Remove this
40+
// ignore and others like it in this file once the linter
41+
// change is active in this project:
42+
// ignore: unreachable_from_main
43+
static void setUpClass() {
44+
expect(didSetUpClass, false);
45+
didSetUpClass = true;
46+
expect(didTearDownClass, false);
47+
}
48+
49+
// TODO(scheglov): See comment directly above
50+
// "TestReflectiveLoaderTest.setUpClass" for info about this ignore:
51+
// ignore: unreachable_from_main
52+
static void tearDownClass() {
53+
expect(didSetUpClass, true);
54+
expect(didTearDownClass, false);
55+
didTearDownClass = true;
56+
}
57+
58+
void test_classwide_state() {
59+
expect(didSetUpClass, true);
60+
expect(didTearDownClass, false);
2261
}
2362

2463
@failingTest
2564
void test_fails() {
2665
expect(false, true);
2766
}
2867

29-
@failingTest
30-
void test_fails_throws_sync() {
68+
@skippedTest
69+
void test_fails_but_skipped() {
3170
throw StateError('foo');
3271
}
3372

@@ -36,13 +75,46 @@ class TestReflectiveLoaderTest {
3675
return Future.error('foo');
3776
}
3877

39-
@skippedTest
40-
void test_fails_but_skipped() {
78+
@failingTest
79+
void test_fails_throws_sync() {
4180
throw StateError('foo');
4281
}
4382

83+
void test_passes() {
84+
expect(true, true);
85+
}
86+
4487
@skippedTest
4588
void test_times_out_but_skipped() {
4689
while (true) {}
4790
}
4891
}
92+
93+
@reflectiveTest
94+
class SecondTest {
95+
static bool didSetUpClass = false;
96+
static bool didTearDownClass = false;
97+
98+
// TODO(scheglov): See comment directly above
99+
// "TestReflectiveLoaderTest.setUpClass" for info about this ignore:
100+
// ignore: unreachable_from_main
101+
static void setUpClass() {
102+
expect(didSetUpClass, false);
103+
didSetUpClass = true;
104+
expect(didTearDownClass, false);
105+
}
106+
107+
// TODO(scheglov): See comment directly above
108+
// "TestReflectiveLoaderTest.setUpClass" for info about this ignore:
109+
// ignore: unreachable_from_main
110+
static void tearDownClass() {
111+
expect(didSetUpClass, true);
112+
expect(didTearDownClass, false);
113+
didTearDownClass = true;
114+
}
115+
116+
void test_classwide_state() {
117+
expect(didSetUpClass, true);
118+
expect(didTearDownClass, false);
119+
}
120+
}

0 commit comments

Comments
 (0)