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);
Description of unexpected behavior:
I have a test which looks something like this:
To my surprise,
fetchedis not set to true (you can assumeFetchSomethingdoesn't throw).Diagnosis of the problem:
await cut.InvokeAsyncin 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 typeAction, whereas for it to be awaited it should be converted toFunc<Task>(which is what I expected).By the way, the implementation of
InvokeAsyncsimply delegates toDispatcher.InvokeAsync, which does provide an overload acceptingFunc<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:and a trivial implementation: