As discussed in #268, the GuardClauseAssertion
class does not wait the tasks to complete, making it unable to validate non fail fast methods.
This is completely understandable, since the fail fast is indeed the best option. Nevertheless, it seems impossible to implement fail fast behavior when dealing with methods marked with the async
keyword.
it seems impossible to implement _fail fast_ behavior when dealing with methods marked with the
async
keyword.
How so? Can you provide an example?
@ploeh Basically, _any_ async
method is not fail-fast. Consider the following example:
```c#
public async Task
{
if (arg == null) throw new ArgumentNullException(nameof(arg));
await Task.Yield();
return default;
}
```
This method doesn't throw if you call it will null
. Instead, the failed Task
is returned. Therefore, the GuardClauseAssertion it its current form fails and complains about missing guard clause.
In real application almost all the code is async
, therefore each invocation is awaited
and exception is thrown immediately. So it's completely OK that "fast-fail" principle is violated in a form that you should await
Task to see exception.
Of course, you can re-write code somehow to make it fail immediately (create private function with async
keyword and move all the logic there), but there is no practical sense to do it.
In real application almost all the code is
async
, therefore each invocation isawaited
and exception is thrown immediately.
That's a fair point; I accept that argument.
I sometimes get sidetracked when someone asserts or implies (with little evidence) that something's _impossible_...
I sometimes get sidetracked when someone asserts or implies (with little evidence) that something's impossible...
Yeah, it's fair enough 😉
Now let me try to think how I can re-design GuardClauseAssertion
to not have boolean flag - I don't like it as well, to be fair. See you later in PR.
Now let me try to think how I can re-design
GuardClauseAssertion
to not have boolean flag
I would think that, as @moodmosaic suggested, a new class might be a better design. I haven't looked hard into the issue, though, so I reserve the right to be wrong 😄
Here is the solution I have been using.
async
keyword of the method signatureasync
keywordExample:
public Task<int> GetNumberAsync(MyObject obj)
{
// Fail-fast behavior
if (request is null)
throw new ArgumentNullException(nameof(obj));
async Task<int> work()
{
var items = await obj.GetItemsAsync(); // whatever you need to call
return items.Count();
}
return work();
}
It has been a good solution for me. Thanks for your help.
@mniak Interesting to have a concrete example of what @zvirja suggested here.
Personally I don't really like it because we are polluting the method with "tricks" for the benefit of the testing framework, which smells really bad to me and I wouldn't want my team to use this pattern.
Keep it open to implement support in a native way. One day. In the nearby future. Or just one day.
Hi. We stumpled over exactly this issue and were suprised that not good solution exists so far.
Are there reasons to not implement a new class for this?
I don't know the inner details of AutoFixture but if we would have a MethodInfo available within the class GuardClauseAssertion for the Method-to-Test we can have a look a its CustomAttributes and identify if it's an Async method or not(https://stackoverflow.com/a/20350646). So, maybe, a switch (bool vs. new class) isn't even necessary.
@zvirja @aivascu could we reconsider this? I really don't see why in 2021 async methods can't be tested properly.
@Kralizek if I read the situation correctly, the request has not been rejected, and there is even a PR that fixes it.
I had a brief look at the PR and I have to say I agree with the feedback it received.
So the only thing left is to either implement the feedback or file a new PR with the feedback implemented.
I couldn't see the linked PR in the mobile app 😅
Most helpful comment
@ploeh Basically, _any_
async
method is not fail-fast. Consider the following example:```c# GuardedOpenGenericMethodReturningTaskResult(object arg)
public async Task
{
if (arg == null) throw new ArgumentNullException(nameof(arg));
}
```
This method doesn't throw if you call it will
null
. Instead, the failedTask
is returned. Therefore, the GuardClauseAssertion it its current form fails and complains about missing guard clause.In real application almost all the code is
async
, therefore each invocation isawaited
and exception is thrown immediately. So it's completely OK that "fast-fail" principle is violated in a form that you shouldawait
Task to see exception.Of course, you can re-write code somehow to make it fail immediately (create private function with
async
keyword and move all the logic there), but there is no practical sense to do it.