Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for concurrency mocking #277

Merged
merged 6 commits into from
Jan 26, 2022

Conversation

ailtonvivaz
Copy link
Contributor

This PR implements support for parsing async methods and closures as discussed in #229

Copy link
Contributor

@andrewchang-bird andrewchang-bird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this PR—looks really good. It seems that Swift is choosing the incorrect overload because the constraint system heavily penalizes mismatched calling contexts (coderef). In the case of XCTests, we’re calling a synchronous method overload from an async context.

I’d like to avoid additional flavors of given and verify to keep the testing API surface as small as possible. After experimenting a bit, I think the cleanest API that also makes sense semantically is something like this:

given(await myMock.doSomethingAsync()).willReturn(someValue)

It does require marking the Mockable<...> framework declarations as async. Let me know what you think.

Edit: Looks like there’s some failing tests due to missing availability annotations.

Comment on lines 345 to 347
if method.attributes.contains(.async) {
attributes += " async"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: single line for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 8 to 21
func XCTAssertThrowsAsyncError<T: Sendable>(
_ expression: @autoclosure () async throws -> T,
_ message: @autoclosure () -> String = "XCTAssertThrowsAsyncError failed: did not throw an error",
file: StaticString = #filePath,
line: UInt = #line,
_ errorHandler: (_ error: Error) -> Void = { _ in }
) async {
do {
_ = try await expression()
XCTFail(message(), file: file, line: line)
} catch {
errorHandler(error)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 348 to 349
if method.attributes.contains(.rethrows) { attributes += " rethrows" }
if method.attributes.contains(.throws) { attributes += " throws" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should preserve the previous semantics as methods can be marked as either rethrows or throws but not both; a rethrowing method implies a throwing method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@andrewchang-bird andrewchang-bird linked an issue Jan 25, 2022 that may be closed by this pull request
2 tasks
@ailtonvivaz
Copy link
Contributor Author

ailtonvivaz commented Jan 26, 2022

Nice, you're right. That's a shame, unfortunately, because the method has the @_disfavoredOverload attribute. 😕

Wow, your idea sounds great.
I am implementing and I will commit soon.
Thanks for the workaround.

There is just one more thing to considering. Do you think this "syntax" should be extended to throwable methods?

given(try await myMock.doSomethingAsyncAndThrowable()).willReturn(someValue)

Even for non async methods?

given(try myMock.doSomethingThrowable()).willReturn(someValue)

Or just for awaitkeyword?

  • Regardless the failing tests due the availability annotation, it happened because of Xcode version. The concurrency backward compatibility is available just to Xcode 13.2 later. I've already updated in github workflow.

@ailtonvivaz
Copy link
Contributor Author

Workaround implemented 🎉
Now, it's possible mock async methods using:

given(await myMock.doSomethingAsync()).willReturn(someValue)

And verify like before as well.

verify(await myMock.doSomethingAsync()).wasCalled()

Copy link
Contributor

@andrewchang-bird andrewchang-bird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! The changes look good to me.

Do you think this "syntax" should be extended to throwable methods?

If Swift’s overload handling changes we might be able to remove await as a requirement when stubbing/verifying, so let’s keep it simple and hold off on adding additional keywords for now.

@andrewchang-bird andrewchang-bird merged commit 2108f83 into typealiased:master Jan 26, 2022
@andrewchang-bird andrewchang-bird added this to the Release 0.20 milestone Jan 26, 2022
@ailtonvivaz ailtonvivaz deleted the async branch May 23, 2022 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Swift concurrency async/await support
2 participants