Replies: 8 comments
-
This should actually work, as far as I can tell. When you call However, you may want to change the code slightly, to ensure that changes to await InvokeAsync(() =>
{
// set other data.
loading = false;
StateHasChanged();
}); This should ensure that the changes you are making in your UI component get dispatched back to the UI thread at a point when the UI thread is free to observe the changes. If the background task is looong running, you can change the timeout for the WaitFor like so. subject.WaitForAssertion(() => _subject.Find(".assert-data"), TimeSpan.FromSeconds(5)); An alternative is to replace the background work being done by a test double that just returns data immediately. That way you are testing your UI functionality in isolation from the background work being done. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the response. I will test this out today and confirm whether it works or not. If it is still giving me trouble I will try and recreate this in a simpler component I can share here. All of our data retrieval methods are being mocked, and their return values should be returning immediately. I did think it was possible we forgot to mock a return value, and the test was getting stuck there, but I've confirmed all data requests are in fact mocked and have a specified return value. |
Beta Was this translation helpful? Give feedback.
-
Question: What are your reasons for spinning up a task on another thread? Why not just do: protected override async Task OnInitializedAsync()
{
loading = true;
await Task.WhenAll(
...async work being done,
...async work to be done on separate thread);
loading = false;
} |
Beta Was this translation helpful? Give feedback.
-
So the UI thread can release while other work is completed. I do also get similar test failures if I await our |
Beta Was this translation helpful? Give feedback.
-
the UI thread is free to run either way, I do not think you get any benefit of explicitly using Task.Run. However, when you do, you have to be careful about where continuations are run, what scheduler is being used. I have never had any reason to do this in my blazor apps, even for slow database queries. |
Beta Was this translation helpful? Give feedback.
-
That is, I dont think you are getting the benefit you think by doing Task.Run.
This works just as well, as far as I know. |
Beta Was this translation helpful? Give feedback.
-
In testing in my particular use case I've found that |
Beta Was this translation helpful? Give feedback.
-
Not true. It will block the rest of the initialization method from running, but the component still completes a render cycle. Take for example this sample component from standard Blazor template: @page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
} This will complete a render cycle and render In other words, you are not achieving what you think you are by creating task with Task.Run and not awaiting them. Learn more here: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-8.0 |
Beta Was this translation helpful? Give feedback.
-
Describe the bug
Testing component with
Task.Run
inside ofasync Task OnInitializedAsync
results in intermittent test failures. When running the test by itself, it will always succeed. When running with all other tests in the class, certain tests will intermittently fail. It's important to note below is an example of what we see causing, we have 14 tests inside our tests class, verifying different aspects of functionality. The tests themselves and the component are more robust, and I can added in a more detailed example if need be. We've also attempted to use bothWaitForState
andWaitForAssertion
in both cases, neither results in the expected behavior. Additionally, adding timespans of upwards of 15 seconds has not fixed the behavior either.I assume this has something to do with trying to run multiple threads with the tests and some asynchronous action locking the completion of the task.
I've verified:
_ = Task.Run
and doing all that work inside the initialization results in the tests passing every time.dotnet tests
command.Example:
Testing this component:
With this test:
Results in this output:
Expected behavior:
I expect the test passes every single time, whether ran individually or ran with all other tests.
Version info:
Additional context:
Using XUnit, FluentAssertions, and MOQ.
Beta Was this translation helpful? Give feedback.
All reactions