NFluent: Check.ThatCode for Task-returning methods does not await result in 3.0.0

Bug Type

Please pick one:

  • a check failed to detect an error (false negative), i.e. a test is green while it should be red.
  • a check raised un existing error (false positive), i.e a test is red while it should be green.
  • an error message is invalid/incomplete (please provide samples)
  • a ran into an exception.
  • other.

Describe the bug

I’m upgrading from 2.7.2 to 3.0.0 and have found I had to migrate .ThatAsyncCode to .ThatCode since the former is Obsolete and I treat warnings as errors. https://www.nuget.org/packages/NFluent/3.0.0.351#readme-body-tab advises that this should replace it (“ThatAsyncCode: you can now use ThatCode even for async methods”)

However, unlike .ThatAsyncCode in the earlier version, .ThatCode does not await the Task for non-generic Task-returning, non-async methods. (However it works fine for Task<T>-returning non-async methods, and also for async methods returning Task).

To Reproduce

    [Test]
    public void Async_issue()
    {
        // simulates the code under test
        async Task<bool> PleaseThrowAsync()
        { 
            await Task.Yield();
            throw new InvalidOperationException("Oh dear!)");
        }
        async Task PleaseThrowAsync2()
        { 
            await Task.Yield();
            throw new InvalidOperationException("Oh dear!)");
        }

        // assert

        // #1 lambda is async method returning Task<T>:
        Check.ThatCode(async () => await PleaseThrowAsync()).Throws<InvalidOperationException>();

        // #2 lambda is non-async method returning Task<T>:
        Check.ThatCode(() => PleaseThrowAsync()).Throws<InvalidOperationException>();

        // #3 lambda is async method returning Task:
        Check.ThatCode(async () => await PleaseThrowAsync2()).Throws<InvalidOperationException>();

        // #4 lambda is non-async method returning Task:
        Check.ThatCode(() => PleaseThrowAsync2()).Throws<InvalidOperationException>();
    }

This throws on the 4th and final Check.ThatCode. For now I can explicitly make the lambda async (ie transform code like check 4 to code like check 3), but I don’t think this is expected to be required.

The equivalent code in 2.7.2 (using ThatAsyncCode instead of ThatCode) does not fail. I encountered this problem when I did a global replace of ThatAsyncCode with ThatCode upon upgrading.

I note that ThatCode<TU>(Func<Task<TU>> awaitableFunc) calls RunTrace.GetAsyncTrace, while ThatCode(Func<Task> awaitableFunc) does not (it calls RunTrace.GetTrace. which then explicitly checks whether the method is async by inspecting whether it has an AsyncStateMachineAttribute). This explains why it works for check 2 but not check 4. Perhaps the fix is simply to change this method to use GetAsyncTrace?

Under the earlier version, ThatAsyncCode(Func<Task> awaitableMethod) called GetAsyncTrace, which explains why it used to work.

Expected behavior Test code above should not fail, and certainly cases 2 and 4 should not be inconsistent.

Desktop (please complete the following information):

  • NFluent version: 3.0.0
  • Net Version: fwk 4.8.2
  • IDE: Rider

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 18

Commits related to this issue

Most upvoted comments

Yep, I am still into this. I may have a way to keep signatures this way, but you are right, it definitely makes more sense to assume Task returning methods are async in nature. I am just looking for a way to check for Task Factory methods.

Long story short, I may have to reintroduce Check.ThatAsyncCode