You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/site/docs/interaction/awaiting-async-state.md
+39-1Lines changed: 39 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,4 +3,42 @@ uid: awaiting-async-state
3
3
title: Awaiting an Asynchronous State Change in a Component Under Test
4
4
---
5
5
6
-
# Awaiting an Asynchronous State Change
6
+
# Awaiting an Asynchronous State Change
7
+
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.
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 [`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.
13
+
14
+
## Waiting for state using `WaitForState`
15
+
16
+
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).
17
+
18
+
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.
1. The test uses a `TaskCompletionSource<string>` to simulate an async web service.
29
+
2. In the second highlighted line, the result is provided to the component through the `textService`. This causes the component to re-render.
30
+
3. In the third highlighted line, the `WaitForState()` method is used to block the test until the predicate provided to it returns true.
31
+
4. Finally, the tests assertion step can execute, knowing that the desired state has been reached.
32
+
33
+
> [!NOTE]
34
+
> 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.
35
+
36
+
### Controlling wait timeout
37
+
38
+
The timeout, which defaults to one second, can be controlled by passing a `TimeSpan` as the second argument to the `WaitForState()` method, e.g.:
title: Triggering a Render Life-Cycle on a Component
3
+
title: Triggering a Render LifeCycle on a Component
4
4
---
5
5
6
-
# Triggering a Render Life-Cycle on a Component
6
+
# Triggering a Render LifeCycle on a Component
7
7
8
-
Describe how to trigger an explicit render life-cycle through the IRenderedComponent Render, SetParametersAndRender, and InvokeAsync methods.
8
+
When a component under test is rendered, an instance of the <xref:Bunit.IRenderedComponent`1> type is returned. Through that, it is possible to cause the component under test to render again directly through the <xref:Bunit.IRenderedComponentBase`1.Render> method or one of the [`SetParametersAndRender(...)`](xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])) methods or indirectly through the <xref:Bunit.IRenderedFragmentBase.InvokeAsync(System.Action)> method.
9
+
10
+
> [!WARNING]
11
+
> The `Render()` and `SetParametersAndRender()` methods are not available in the <xref:Bunit.IRenderedFragment> type that is returned when calling the _non_-generic version of `GetComponentUnderTest()` in `<Fixture>`-based Razor tests. Call the generic version of `GetComponentUnderTest<TComponent>()` to get a <xref:Bunit.IRenderedComponent`1>.
12
+
13
+
> [!NOTE]
14
+
> These methods are available and work the same in both C# and Razor-based tests. The examples below are from C# based tests only.
15
+
16
+
Let's look at how to use each of these methods to cause a re-render.
17
+
18
+
## Render
19
+
20
+
The <xref:Bunit.IRenderedComponentBase`1.Render> tells the renderer to re-render the component, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods). To use it, do the following:
The highlighted line shows the call to <xref:Bunit.IRenderedComponentBase`1.Render>.
25
+
26
+
> [!TIP]
27
+
> The number of renders a component has been through can be inspected and verified using the <xref:Bunit.IRenderedFragmentBase.RenderCount> property.
28
+
29
+
## SetParametersAndRender
30
+
31
+
The [`SetParametersAndRender(...)`](xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])) methods tells the renderer to re-render the component with new parameters, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods), passing the new parameters to the `SetParametersAsync()` method, _but only the new parameters_. To use it, do the following:
The highlighted line shows the call to <xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(System.Action{Bunit.ComponentParameterBuilder{`0}})>, which is also available as <xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])> if you prefer that method of passing parameters.
36
+
37
+
> [!NOTE]
38
+
> Passing parameters to components through the [`SetParametersAndRender(...)`](xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])) methods is identical to doing it with the [`RenderComponent<TComponent>(...)`](xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])) methods, described in detail on the <xref:passing-parameters-to-components> page.
39
+
40
+
## InvokeAsync
41
+
42
+
Invoking methods on a component under test, which causes a render, e.g. by calling `StateHasChanged`, can result in the following error:
43
+
44
+
> The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.
45
+
46
+
If you receive this error, you need to invoke your method inside an `Action` delegate passed to the <xref:Bunit.IRenderedFragmentBase.InvokeAsync(System.Action)> method.
47
+
48
+
Consider the `<ImparativeCalc>` component listed below:
The highlighted line shows the call to <xref:Bunit.IRenderedFragmentBase.InvokeAsync(System.Action)>, which is passed an `Action` delegate, that calls the `Calculate` method.
57
+
58
+
> [!TIP]
59
+
> The instance of a component under test is available through the <xref:Bunit.IRenderedComponentBase`1.Instance> property.
0 commit comments