Unit testing of this trivial GlobalActor always fails when I run more than one unit test, but always success (I expect the test to pass) when just running once.
final actor MyGlobalActor: GlobalActor {
static let shared = MyGlobalActor()
public var isNew: Bool = true
private init() {}
func isNotNew() async {
self.isNew = false
}
}
final class GlobalActorTests: XCTestCase {
func test_0() async {
await doTest()
}
func test_1() async {
await doTest()
}
func doTest() async {
var isNew = await MyGlobalActor.shared.isNew
XCTAssertTrue(isNew)
await MyGlobalActor.shared.isNotNew()
isNew = await MyGlobalActor.shared.isNew
XCTAssertFalse(isNew)
}
}
How can I... "reset" or otherwise "tearDown" the GlobalActor between each unit test? What are the recommendations for getting these trivial tests to pass?
Global actors are global: the same instance is reused for all your tests run from the same process. It's indeed difficult to isolate tests when they share mutable state
In your test cases, did you try overriding setUp(completion:), and reset the global actor to a pristine state before each test (I did not run the following code but you get the idea)?
final actor MyGlobalActor: GlobalActor {
func reset() {
isNew = true
}
}
final class GlobalActorTests: XCTestCase {
override func setUp() async throws {
await MyGlobalActor.shared.reset()
}
func test_0() async { ... }
...
}
Also, I guess you should disable parallel testing.
I think that you might be asking for the impossible. Declaring the initialiser of MyGlobalActor as private is a design decision. That decision is now constraining the type of tests that you can write.
The private initialiser means that there can only be one instance of MyGlobalActor, so you can only test the effect of the isNotNew on one occasion. Even this is fraught with danger due to test parallelisation and random ordering.
This is unfortunately not an option, since this was a complete simplification of my rather much more complex actor, which contains Buffered AsyncSequence, I want to check how they behave from "fresh start".
I was hoping I could somehow use some XCTest API I did not not about to... "restart XCTest run" completely, but I guess that is not possible..?
And this unit test is in a SPM testTarget, I believe parallelisation is disabled by default for those?
As far as I know, multiple XCTestCase subclasses can be run in parallel, but the test methods of each subclass will run sequentially (in alphabetical or random order).
If you moved each test method to its own subclass, and enabled the "execute in parallel" option, there might be a separate process (and global actor) for each test.
For what it's worth that's why anything "global" isn't great for parallel execution of different "world views" like for example isolated testing of anything.
You might be better off with using normal instance actors, even if it means more "passing around things".