Skip to content

Commit a20d0a2

Browse files
committed
Merge branch 'main' into dev
# Conflicts: # docs/samples/tests/xunit/VerifyMarkupExamples.cs # docs/site/docs/verification/verify-component-state.md # docs/site/docs/verification/verify-markup.md
2 parents 99a97c5 + 8edfff2 commit a20d0a2

File tree

7 files changed

+112
-18
lines changed

7 files changed

+112
-18
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>Hello World</p>"), TimeSpan.FromSeconds(2));
67+
}
5168
}
5269
}

docs/samples/tests/xunit/VerifyMarkupExamples.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,4 @@ public void GetChangesSinceX()
141141
.ShouldBeRemoval("<li>Second item</li>");
142142
}
143143
}
144-
}
144+
}

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.

docs/site/docs/verification/verify-component-state.md

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,49 @@ uid: verify-component-state
33
title: Verifying the State of a Component Under Test
44
---
55

6-
# Verifying the State of a Component Under Test
6+
# Verifying the State of a Component
77

8-
Describe IRenderedComponent interface
8+
Calling [`RenderComponent<TComponent>`()](xref:Bunit.TestContext.RenderComponent``1(System.Action{Bunit.ComponentParameterBuilder{``0}})) on a <xref:Bunit.TestContext> or calling <xref:Bunit.Fixture.GetComponentUnderTest``1> on a <xref:Bunit.Fixture> returns an instance of the <xref:Bunit.IRenderedComponent`1> type.
99

10-
Show Instance prop
10+
The <xref:Bunit.IRenderedComponent`1> type makes it possible to inspect the instance of the component under test (`TComponent`), and trigger re-renders explicitly.
1111

12-
Warn against setting props directly
12+
> [!NOTE]
13+
> Since <xref:Bunit.IRenderedComponent`1> inherits from <xref:Bunit.IRenderedFragment>, all the markup verification techniques covered on the <xref:verify-markup> page also applies to the <xref:Bunit.IRenderedComponent`1> type.
1314
14-
FindComponent/FindComponents
15+
## Inspecting the Component Under Test
16+
17+
The <xref:Bunit.IRenderedComponentBase`1.Instance> property on the <xref:Bunit.IRenderedComponent`1> type provides access to the component under test. For example:
18+
19+
```csharp
20+
using var ctx = new TestContext();
21+
22+
IRenderedComponent<Alert> cut = ctx.RenderComponent<Alert>();
23+
24+
Alert alert = cut.Instance;
25+
26+
// Assert against <Alert /> instance
27+
```
28+
29+
> [!WARNING]
30+
> While it is possible to set `[Parameter]` and `[CascadingParameter]` properties directly through the <xref:Bunit.IRenderedComponentBase`1.Instance> property on the <xref:Bunit.IRenderedComponent`1> type, doing so does not implicitly trigger a render and the component life-cycle methods are not called.
31+
>
32+
> The correct approach is to set parameters through the [`SetParametersAndRender()`](xref:Bunit.IRenderedComponentBase`1.SetParametersAndRender(Bunit.Rendering.ComponentParameter[])) methods. See the <xref:trigger-renders> page for more on this.
33+
34+
## Finding Components in the Render Tree
35+
36+
To get the instance of components nested inside the component under test, use the
37+
[`FindComponent<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponent``1(Bunit.IRenderedFragment)) and [`FindComponents<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponents``1(Bunit.IRenderedFragment)) methods on the <xref:Bunit.IRenderedComponent`1> type. Suppose we have a `<TodoList>` component with `<Task>` components nested inside for each task in the todo list, then the `<Task>` components can be found like this:
38+
39+
```csharp
40+
using var ctx = new TestContext();
41+
var cut = ctx.RenderComponent<TodoList>(parameter => parameter
42+
.Add(p => p.Tasks, new [] { "Task 1", "Task 2" })
43+
);
44+
45+
var tasks = cut.FindComponents<Task>();
46+
47+
Assert.Equal(2, tasks.Count);
48+
```
49+
50+
Both the [`FindComponent<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponent``1(Bunit.IRenderedFragment)) and [`FindComponents<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponents``1(Bunit.IRenderedFragment)) methods performs a **depth-first search** of the render tree, with the first method returning only the first found matching component, while latter returning all matching components in the render tree.
51+
Both the [`FindComponent<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponent``1(Bunit.IRenderedFragment)) and [`FindComponents<TComponent>()`](xref:Bunit.RenderedFragmentExtensions.FindComponents``1(Bunit.IRenderedFragment)) methods performs a **depth-first search** of the render tree, with the first method returning only the first found matching component, while latter returning all matching components in the render tree.

docs/site/docs/verification/verify-markup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ This is what happens in the test:
177177
7. A new snapshot is saved, replacing the previous one, with a another call to the <xref:Bunit.IRenderedFragment.SaveSnapshot> method.
178178
8. Finally the last item in the list is found and clicked, and the <xref:Bunit.IRenderedFragment.GetChangesSinceSnapshot> method is used to find the changes, a single diff, which is verified as a removal of the second item.
179179

180-
As mentioned earlier, the `IDiff` assertion helpers are still experimental. Any feedback and suggestions for improvements should be directed to the [related issue](https://github.com/egil/bUnit/issues/84) on GitHub.
180+
As mentioned earlier, the `IDiff` assertion helpers are still experimental. Any feedback and suggestions for improvements should be directed to the [related issue](https://github.com/egil/bUnit/issues/84) on GitHub.

0 commit comments

Comments
 (0)