Open TysonMN opened 1 year ago
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.
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#)?
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 🤷
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.
@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
.
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!
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 😉
A relevant excerpt from the fsharp-hedgehog-xunit docs:
If the test returns
Async<_>
orTask<_>
, thenAsync.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)
Grepping FsCheck for Async.AwaitTask
and Async.RunSynchronously
yields nothing interesting, which makes me think they're handling it more intelligently than I am.
Requested by @ploeh in this tweet.