Skip to content

Commit 2cc9a8b

Browse files
committed
Docs: async assertions
1 parent 0ee079d commit 2cc9a8b

File tree

4 files changed

+68
-11
lines changed

4 files changed

+68
-11
lines changed

docs/samples/tests/xunit/AsyncDataTest.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public void LoadDataAsync()
1616
var textService = new TaskCompletionSource<string>();
1717
var cut = ctx.RenderComponent<AsyncData>(parameters => parameters
1818
.Add(p => p.TextService, textService.Task)
19-
);
19+
);
2020

2121
// Act - set the awaited result from the text service
2222
textService.SetResult("Hello World");
@@ -27,7 +27,7 @@ public void LoadDataAsync()
2727
// Assert - verify result has been set
2828
cut.MarkupMatches("<p>Hello World</p>");
2929
}
30-
30+
3131
[Fact]
3232
public void LoadDataAsyncWithTimeout()
3333
{
@@ -36,7 +36,7 @@ public void LoadDataAsyncWithTimeout()
3636
var textService = new TaskCompletionSource<string>();
3737
var cut = ctx.RenderComponent<AsyncData>(parameters => parameters
3838
.Add(p => p.TextService, textService.Task)
39-
);
39+
);
4040

4141
// Act - set the awaited result from the text service
4242
textService.SetResult("Long time");
@@ -47,6 +47,23 @@ public void LoadDataAsyncWithTimeout()
4747
// Assert - verify result has been set
4848
cut.MarkupMatches("<p>Long time</p>");
4949
}
50-
50+
51+
[Fact]
52+
public void LoadDataAsyncAssertion()
53+
{
54+
// Arrange
55+
using var ctx = new TestContext();
56+
var textService = new TaskCompletionSource<string>();
57+
var cut = ctx.RenderComponent<AsyncData>(parameters => parameters
58+
.Add(p => p.TextService, textService.Task)
59+
);
60+
61+
// Act - set the awaited result from the text service
62+
textService.SetResult("Hello World");
63+
64+
// Wait for assertion to pass
65+
cut.WaitForAssertion(() => cut.MarkupMatches("<p>Hello World</p>"));
66+
cut.WaitForAssertion(() => cut.MarkupMatches("<p>xHello World</p>"), TimeSpan.FromSeconds(2));
67+
}
5168
}
5269
}

docs/site/docs/interaction/awaiting-async-state.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ title: Awaiting an Asynchronous State Change in a Component Under Test
55

66
# Awaiting an Asynchronous State Change
77

8-
A test can fail if a component performs asynchronous renders, e.g. because it was awaiting an task to complete before continuing its render life-cycle. For example, if a component is waiting for a async web service to return data to it in the `OnInitializedAsync()` life-cycle method, before rendering it to the render tree.
8+
A test can fail if a component performs asynchronous renders, e.g. because it was awaiting a task to complete before continuing its render life-cycle. For example, if a component is waiting for an async web service to return data to it in the `OnInitializedAsync()` life-cycle method, before rendering it to the render tree.
99

1010
This happens because tests execute in the test framework's synchronization context and the test renderer executes renders in its own synchronization context.
1111

1212
bUnit comes with two methods that helps deal with this issue, the [`WaitForState(Func<Boolean>, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragmentBase,System.Func{System.Boolean},System.Nullable{System.TimeSpan})) method covered on this page, and the [`WaitForAssertion(Action, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragmentBase,System.Action,System.Nullable{System.TimeSpan})) method covered on the <xref:async-assertion> page.
1313

14-
## Waiting for state using `WaitForState`
14+
## Waiting for State Using `WaitForState`
1515

1616
The [`WaitForState(Func<Boolean>, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragmentBase,System.Func{System.Boolean},System.Nullable{System.TimeSpan})) method can be used to block and wait in a test method, until the provided predicate returns true, or the timeout is reached (the default timeout is one second).
1717

18+
> [!NOTE]
19+
> The `WaitForState()` method will try the predicate pass to it when the `WaitForState()` method is called, and every time the component under test renders.
20+
1821
Let us look at an example. Consider the following `<AsyncData>` component, who awaits an async `TextService` in its `OnInitializedAsync()` life-cycle method. When the service returns the data, the component will automatically re-render, to update its rendered markup.
1922

2023
[!code-html[AsyncData.razor](../../../samples/components/AsyncData.razor)]
@@ -30,10 +33,10 @@ This is what happens in the test:
3033
3. In the third highlighted line, the `WaitForState()` method is used to block the test until the predicate provided to it returns true.
3134
4. Finally, the tests assertion step can execute, knowing that the desired state has been reached.
3235

33-
> [!NOTE]
36+
> [!WARNING]
3437
> The wait predicate and an assertion should not verify the same thing. Instead, use the [`WaitForAssertion(...)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragmentBase,System.Action,System.Nullable{System.TimeSpan})) method covered on the <xref:async-assertion> page instead.
3538
36-
### Controlling wait timeout
39+
### Controlling Wait Timeout
3740

3841
The timeout, which defaults to one second, can be controlled by passing a `TimeSpan` as the second argument to the `WaitForState()` method, e.g.:
3942

docs/site/docs/toc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
## [Verify Markup](xref:verify-markup)
2020
## [Verify Component State](xref:verify-component-state)
2121
## [Customizing Semantic Comparison](xref:semantic-html-comparison)
22-
## [Asynchronous Assertion of Changes](xref:async-assertion)
22+
## [Assertion of Asynchronous Changes](xref:async-assertion)
2323

2424
# [Test Doubles](xref:test-doubles)
2525
## [Faking Authorization](xref:faking-auth)
Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,43 @@
11
---
22
uid: async-assertion
3-
title: Asynchronous Assertion of Change in a Component Under Test
3+
title: Assertion of Asynchronous Changes
44
---
55

6-
# Asynchronous Assertion of Change in a Component Under Test
6+
# Assertion of Asynchronous Changes
7+
8+
A test can fail if a component performs asynchronous renders, e.g. because it was awaiting a task to complete before continuing its render life-cycle. For example, if a component is waiting for an async web service to return data to it in the `OnInitializedAsync()` life-cycle method, before rendering it to the render tree.
9+
10+
This happens because tests execute in the test framework's synchronization context and the test renderer executes renders in its own synchronization context.
11+
12+
bUnit comes with two methods that helps deal with this issue, the [`WaitForAssertion(Action, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragmentBase,System.Action,System.Nullable{System.TimeSpan})) method covered on this page, and the [`WaitForState(Func<Boolean>, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragmentBase,System.Func{System.Boolean},System.Nullable{System.TimeSpan})) method covered on the <xref:awaiting-async-state> page.
13+
14+
## Waiting for Assertion to Pass Using `WaitForAssertion`
15+
16+
The [`WaitForAssertion(Action, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragmentBase,System.Action,System.Nullable{System.TimeSpan})) method can be used to block and wait in a test method, until the provided assert action does not throw an exception, or the timeout is reached (the default timeout is one second).
17+
18+
> [!NOTE]
19+
> The `WaitForAssertion()` method will try the assert action pass to it when the `WaitForAssertion()` method is called, and every time the component under test renders.
20+
21+
Let us look at an example. Consider the following `<AsyncData>` component, who awaits an async `TextService` in its `OnInitializedAsync()` life-cycle method. When the service returns the data, the component will automatically re-render, to update its rendered markup.
22+
23+
[!code-html[AsyncData.razor](../../../samples/components/AsyncData.razor)]
24+
25+
To test the `<AsyncData>` component, do the following:
26+
27+
[!code-csharp[AsyncDataTest.cs](../../../samples/tests/xunit/AsyncDataTest.cs?start=54&end=65&highlight=3,9,12)]
28+
29+
This is what happens in the test:
30+
31+
1. The test uses a `TaskCompletionSource<string>` to simulate an async web service.
32+
2. In the second highlighted line, the result is provided to the component through the `textService`. This causes the component to re-render.
33+
3. Finally, in the third highlighted line, the `WaitForAssertion()` method is used to block the test until the predicate assertion action runs without throwing an exception.
34+
35+
### Controlling Wait Timeout
36+
37+
The timeout, which defaults to one second, can be controlled by passing a `TimeSpan` as the second argument to the `WaitForAssertion()` method, e.g.:
38+
39+
[!code-csharp[](../../../samples/tests/xunit/AsyncDataTest.cs?start=66&end=66)]
40+
41+
If the timeout is reached, a <xref:Bunit.Extensions.WaitForHelpers.WaitForFailedException> exception is thrown with the following error message:
42+
43+
> The assertion did not pass within the timeout period.

0 commit comments

Comments
 (0)