Skip to content

Add overload IRenderedComponentBase.InvokeAsync(Func<Task>) #166

@JeroenBos

Description

@JeroenBos

Description of unexpected behavior:

I have a test which looks something like this:

[Fact]
async Task MyTest()
{
    IRenderedComponent<MyComponent> cut = ...;
    bool fetched = false;
    await cut.InvokeAsync(async () => {
        await FetchSomething();
        fetched = true;
    });
    
    Assert(fetched == true);  // throws!!
}

To my surprise, fetched is not set to true (you can assume FetchSomething doesn't throw).

Diagnosis of the problem:

await cut.InvokeAsync in fact doesn't await the provided callback.
That's because it has the signature Task InvokeAsync(Action callback) and so the lambda I provide above is converted to type Action, whereas for it to be awaited it should be converted to Func<Task> (which is what I expected).
By the way, the implementation of InvokeAsync simply delegates to Dispatcher.InvokeAsync, which does provide an overload accepting Func<Task>.

Proposed solution:

In my opinion, await cut.InvokeAsync(async () => { ... }) actually awaits the lambda in idiomatic C#. To enable this desired behavior, I propose to add an overload:

// in bunit.core/IRenderedComponentBase.cs:

/// <summary>
/// Invokes the given <paramref name="callback"/> in the context of the associated <see cref="ITestRenderer"/>.
/// </summary>
/// <param name="callback"></param>
/// <returns>A <see cref="Task"/> that will be completed when the action has finished executing.</returns>
Task InvokeAsync(Func<Task> callback);

and a trivial implementation:

// in bunit.web/Rendering/RenderedComponent.cs:

/// <inheritdoc/>
public Task InvokeAsync(Func<Task> callback) => Renderer.Dispatcher.InvokeAsync(callback);

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions