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

Support asynchronous computations: Async and Task #428

Open
TysonMN opened this issue Mar 18, 2023 · 8 comments
Open

Support asynchronous computations: Async and Task #428

TysonMN opened this issue Mar 18, 2023 · 8 comments

Comments

@TysonMN
Copy link
Member

TysonMN commented Mar 18, 2023

Requested by @ploeh in this tweet.

@ploeh
Copy link
Contributor

ploeh commented Mar 18, 2023

Thank you for creating this issue. In case Twitter stops working, here's the full contents of the tweet:

@tyson_mn @nikosbaxevanis How do I write an asynchronous #fsharp Hedgehog property (preferably with System.Threading.Tasks)?

(Links automatically created by the copy-paste action from Twitter.)

FWIW, Task-based properties are possible in FsCheck 3 (which is in perpetual beta) - just in case someone wants some inspiration.

@TysonMN
Copy link
Member Author

TysonMN commented Mar 18, 2023

Why do you want a Task-based API (instead of wanting an Async-based API and converting your Task to Async, which would be idiomatic F#)?

@ploeh
Copy link
Contributor

ploeh commented Mar 18, 2023

Everything that's asynchronous in .NET is Task-based, and it's in the context of testing things like that that I need the feature. F# now has a task computation expression in addition to async, so regardless on what one might feel about the advantages and drawbacks of the two options, I think it's safe to assume that at this point, F# Async is legacy. It's Betamax versus VHS all over again 🤷

@cmeeren
Copy link
Contributor

cmeeren commented Apr 1, 2023

I think it's safe to assume that at this point, F# Async is legacy. It's Betamax versus VHS all over again 🤷

What makes you say that? Async still has benefits (implicit cancellation token passing and tail recursion), and Async programming in F# says explicity to prefer Async:

In general, you should use async { } programming in F# unless you frequently need to create or consume .NET tasks.

@ploeh
Copy link
Contributor

ploeh commented Apr 2, 2023

@cmeeren, fair enough, Async does have its uses. In fact, I can even think of a few myself, where Async would still be better than Task, but they're uncommon. I could have been more nuanced in my wording.

To be clear, I wouldn't mind if Hedgehog supports testing against Async in addition to Task, but I don't expect to use it much.

When I need to test something asynchronous, it's always because I'm working with some kind of .NET API, and they're always Task-based. Here's a typical example: Self-hosted integration tests in ASP.NET. These tests are almost always integration tests (or whatever you want to call them) that test how multiple components integrate with framework code. When working on that level, I can't choose between Async and Task; the latter is a given.

As soon as I change detail level to write smaller tests (unit tests), I know how to design code so that it's not asynchronous at all. Thus, I don't need this feature for unit testing; I need it for integration testing, and therefore I need it for Task.

@cmeeren
Copy link
Contributor

cmeeren commented Apr 2, 2023

Thanks for the clarification. I find it interesting that you use Hedgehog (property-driven, lots of test repetitions) for integration tests. I'd be interested in reading an article on that at some point!

@ploeh
Copy link
Contributor

ploeh commented Apr 2, 2023

I find it interesting that you use Hedgehog (property-driven, lots of test repetitions) for integration tests.

I don't, because I can't (because I can't write asynch properties). I use FsCheck instead.

I'd be interested in reading an article on that at some point!

Keep an eye on this article series 😉

@dharmaturtle
Copy link
Member

A relevant excerpt from the fsharp-hedgehog-xunit docs:

If the test returns Async<_> or Task<_>, then Async.RunSynchronously is called, which blocks the thread. This may have significant performance implications as tests run 100 times by default.

[<Property>]
let ``Async with exception shrinks`` (i: int) = async {
  do! Async.Sleep 100
  if i > 10 then
    failwith "whoops!"
  }

=== Output ===
Hedgehog.FailedException: *** Failed! Falsifiable (after 12 tests):
(11)

Relevant source code.

Grepping FsCheck for Async.AwaitTask and Async.RunSynchronously yields nothing interesting, which makes me think they're handling it more intelligently than I am.

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

No branches or pull requests

4 participants