-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathasync-assertion.html
More file actions
182 lines (157 loc) · 11.2 KB
/
Copy pathasync-assertion.html
File metadata and controls
182 lines (157 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Assertion of asynchronous changes | bUnit </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Assertion of asynchronous changes | bUnit ">
<meta name="description" content="bUnit is a unit testing library for Blazor Components. You can easily define components under test in C# or Razor syntax and verify outcome using semantic HTML diffing/comparison logic. You can interact with and inspect components, trigger event handlers, provide cascading values, inject services, mock IJsRuntime, and perform snapshot testing.">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="manifest" href="/site.webmanifest">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="../toc.html">
<meta name="docfx:rel" content="../../">
<meta name="docfx:docurl" content="https://github.com/bUnit-dev/bUnit/blob/stable/docs/site/docs/verification/async-assertion.md/#L1">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
<meta name="loc:searchNoResults" content="No results for "{query}"">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
</head>
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../..//images/bunit-logo.png" alt="bUnit">
bUnit
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled="" placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" style="margin-top: -.65em; margin-left: -.8em" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="async-assertion">
<h1 id="assertion-of-asynchronous-changes">Assertion of asynchronous changes</h1>
<p>A test can fail if a component performs asynchronous renders. This may be due to a reason such as waiting for an asynchronous operation 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 <code>OnInitializedAsync()</code> life-cycle method before rendering it to the render tree.</p>
<p>You need to handle this specifically in your tests because tests execute in the test framework's synchronization context and the test renderer executes renders in its own synchronization context. If you do not, you will likely experience tests that sometimes pass, and sometimes fail.</p>
<p>bUnit comes with two methods that help to deal with this issue: the <a class="xref" href="../../api/Bunit.RenderedComponentWaitForHelperExtensions.html#Bunit_RenderedComponentWaitForHelperExtensions_WaitForAssertion__1_Bunit_IRenderedComponent___0__System_Action_System_Nullable_System_TimeSpan__"><code>WaitForAssertion()</code></a> method covered on this page, and the <a class="xref" href="../../api/Bunit.RenderedComponentWaitForHelperExtensions.html#Bunit_RenderedComponentWaitForHelperExtensions_WaitForState__1_Bunit_IRenderedComponent___0__System_Func_System_Boolean__System_Nullable_System_TimeSpan__"><code>WaitForState()</code></a> method covered on the <a class="xref" href="../interaction/awaiting-async-state.html">Awaiting an asynchronous state change in a component under test</a> page.</p>
<p>Let's start by taking a look at the <code> WaitForAssertion</code> method in more detail.</p>
<h2 id="waiting-for-assertion-to-pass-using-waitforassertion">Waiting for assertion to pass using <code>WaitForAssertion</code></h2>
<p>The <a class="xref" href="../../api/Bunit.RenderedComponentWaitForHelperExtensions.html#Bunit_RenderedComponentWaitForHelperExtensions_WaitForAssertion__1_Bunit_IRenderedComponent___0__System_Action_System_Nullable_System_TimeSpan__"><code>WaitForAssertion(Action, TimeSpan?)</code></a> method can be used to block and wait in a test method until the provided assert action does not throw an exception, or until the timeout is reached (the default timeout is one second).</p>
<div class="NOTE">
<h5>Note</h5>
<p>The <code>WaitForAssertion()</code> method will try the assert action passed to it when the <code>WaitForAssertion()</code> method is called and every time the component under test renders.</p>
</div>
<p>Let's look at an example. Consider the following <code><AsyncData></code> component, which awaits an async <code>TextService</code> in its <code>OnInitializedAsync()</code> life-cycle method. When the service returns the data, the component will automatically re-render to update its rendered markup.</p>
<pre><code class="lang-cshtml" name="AsyncData.razor"><p>@text</p>
@code
{
string text = string.Empty;
[Parameter] public Task<string> TextService { get; set; }
protected override async Task OnInitializedAsync()
{
text = await TextService;
}
}
</code></pre>
<p>To test the <code><AsyncData></code> component, do the following:</p>
<pre><code class="lang-csharp" name="AsyncDataTest.cs" highlight-lines="2,8,11">// Arrange
var textService = new TaskCompletionSource<string>();
var cut = Render<AsyncData>(parameters => parameters
.Add(p => p.TextService, textService.Task)
);
// Act - set the awaited result from the text service
textService.SetResult("Hello World");
// Wait for assertion to pass
cut.WaitForAssertion(() => cut.MarkupMatches("<p>Hello World</p>"));
</code></pre>
<p>This is what happens in the test:</p>
<ol>
<li>The test uses a <code>TaskCompletionSource<string></code> to simulate an async web service.</li>
<li>In the second highlighted line, the result is provided to the component through the <code>textService</code>. This causes the component to re-render.</li>
<li>Finally, in the third highlighted line, the <code>WaitForAssertion()</code> method is used to block the test until the predicate assertion action runs without throwing an exception.</li>
</ol>
<h3 id="controlling-wait-timeout">Controlling wait timeout</h3>
<p>The timeout, which defaults to one second, can be controlled by passing a <code>TimeSpan</code> as the second argument to the <code>WaitForAssertion()</code> method, e.g.:</p>
<pre><code class="lang-csharp">cut.WaitForAssertion(() => cut.MarkupMatches("<p>Hello World</p>"), TimeSpan.FromSeconds(2));
</code></pre>
<p>If the timeout is reached, a <a class="xref" href="../../api/Bunit.Extensions.WaitForHelpers.WaitForFailedException.html">WaitForFailedException</a> exception is thrown with the following error message:</p>
<blockquote>
<p>The assertion did not pass within the timeout period.</p>
</blockquote>
<p>Setting the timeout to something less than one second does <em>not</em> make tests run faster. The <code>WaitForAssertion()</code> method returns as soon as it observes the predicate assertion running without throwing. So, it is generally only useful to set a different timeout than the default if the asynchronous operation takes longer than one second to complete, which should only be an issue in end-2-end or integration-testing scenarios.</p>
</article>
<div class="contribution d-print-none">
<a href="https://github.com/bUnit-dev/bUnit/blob/stable/docs/site/docs/verification/async-assertion.md/#L1" class="edit-link">Edit this page</a>
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
<div class="border-top text-center pt-4">
<a class="d-block" href="https://dotnetfoundation.org" title=".NET Foundation">
<img width="150" src="/sponsors/dotnetfoundation_v4_purple.svg" alt=".NET Foundation">
</a>
<p class="pt-1"><small>Supported by the <a href="https://dotnetfoundation.org">.NET Foundation</a>.</small></p>
</div>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary bg-body-tertiary">
<div class="container-xxl">
<div class="flex-fill">
<small>Documentation updated on 2026-08-10T12:54:03.0000000+00:00 in commit d4d81d9e5b</small>
</div>
</div>
</footer>
</body>
</html>