I’ve recently turned on Swift 6.0 mode on of my projects and that means migrating all code to strict concurrency. I had some tests that used mocks to capture values and then using an #expect
macro to
check whether the captured values were the correct ones. One such test looked like this:
@Test
func loginWithNoAccountsAndSuccessfulLoginSavesTokenInCredentialManager() async throws
{
let expectedCredentials = AccountCredentials(account: "test@instance.social",server: "instance.social", token: "mynewaccesstoken")
var instanceUrlCalled: URL?
var storedCredentials: AccountCredentials?
let appModel = withDependencies {
$0.defaultDatabase = try! appDatabase()
$0.socialMediaClient = MockSocialMediaServer(connectToInstance: { url in
instanceUrlCalled = url
return expectedCredentials
})
$0.accountCredentialsStorage.storeCredentials = { credentials in storedCredentials = credentials
}
} operation: {
AppModel()
}
#expect(appModel.destination == nil)
try await appModel.login()
guard let expectedUrl = URL(string: "https://instance.social") else {
Issue.record("Failed to creater URL from string")
return
}
#expect(appModel.destination.is(\.onboardingScreen))
#expect(expectedUrl == instanceUrlCalled)
#expect(expectedCredentials == storedCredentials)
}
However, capturing these variables caused problems in strict concurrency mode. I could lead to potential
data races and therefore swift 6.0 does not allow it. It took me some time to figure out how to fix this, and it turned out you shouldn’t capture any values at all! The fix was as easy as putting the #expect
macros in the closures passed to the mock, like this: