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/csharp-examples.md
+207-7Lines changed: 207 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,10 +6,14 @@ All examples can be found in the [CodeOnlyTests](../sample/tests/CodeOnlyTests)
6
6
7
7
1.[Creating new test classes](#creating-new-test-classes)
8
8
2.[Testing components without parameters](#testing-components-without-parameters)
9
-
3.[Testing components with regular parameters](#testing-components-with-regular-parameters)
9
+
3.[Testing components with parameters](#testing-components-with-parameters)
10
+
3.1. [Passing new parameters to an already rendered component](#passing-new-parameters-to-an-already-rendered-component)
10
11
4.[Testing components with child content](#testing-components-with-child-content)
11
12
5.[Testing components with EventCallback parameters](#testing-components-with-eventcallback-parameters)
12
13
6.[Testing components with cascading-value parameters](#testing-components-with-cascading-value-parameters)
14
+
7.[Testing components that use on IJsRuntime](#testing-components-that-use-on-ijsruntime)
15
+
7.1 [Verifying element references](#verifying-element-references)
16
+
8.[Testing components with injected dependencies]()
13
17
14
18
## Creating new test classes
15
19
@@ -129,7 +133,7 @@ A few things worth noting about the tests above:
129
133
130
134
With the _targeted_ version, we cannot guarantee that there are not other changes in other places of the rendered HTML, if that is a concern, use the strict style. If it is not, then the targeted style can lead to simpler test.
131
135
132
-
## Testing components with regular parameters
136
+
## Testing components with parameters
133
137
134
138
In the following tests we will pass regular parameters to a component under test, e.g. `[Parameter] public SomeType PropName { get; set; }` style properties, where `SomeType`**is not** a `RenderFragment` or a `EventCallback` type.
135
139
@@ -190,15 +194,57 @@ As highlighted in the code, I recommend using the [`nameof`](https://docs.micros
190
194
191
195
The second parameter, `class` is explicitly declared in the `Aside` class. It is instead `Attributes` parameter, that captures all unmatched parameters.
192
196
197
+
### Passing new parameters to an already rendered component
198
+
199
+
Sometimes we want to test what happens when a component is re-rendered, possible with new parameters. This can be done using the `cut.Render()` and the `cut.SetParametersAndRender()` methods, for example:
200
+
201
+
```csharp
202
+
[Fact(DisplayName="Passing new parameters to Aside updates the rendered HTML correctly")]
203
+
publicvoidTest002()
204
+
{
205
+
// Arrange - initial render of Aside
206
+
varcut=RenderComponent<Aside>();
207
+
208
+
// Act - set the Header parameter and re-render the CUT
// Assert - Check that we have exactly one change since compared with the snapshot we took,
224
+
// and that it is an addition to the DOM tree.
225
+
cut.GetChangesSinceSnapshot()
226
+
.ShouldHaveSingleChange()
227
+
.ShouldBeRemoval("<header>HEADER</header>");
228
+
}
229
+
```
230
+
231
+
Some notes on `Test002` above:
232
+
233
+
- The `cut.SetParametersAndRender()` method has the same overloads as the `RenderComponent()` method.
234
+
- The `ShouldHaveSingleChange()` method asserts that only a single difference is found by the compare method, and returns that diff object.
235
+
- The `ShouldBeAddition()` method verifies that a difference is an addition with the specified content (doing a semantic HTML comparison).
236
+
- The `cut.TakeSnapshot()` method saves the current rendered HTML for later comparisons.
237
+
- The `cut.GetChangesSinceSnapshot()` compares the current rendered HTML with the one saved by the `TakeSnapshot()` method.
238
+
193
239
## Testing components with child content
194
240
195
241
The [Aside.razor](../sample/src/Components/Aside.razor) component listed in the previous section also has a `ChildContent` parameter, so lets add a few tests that passes markup and components to it through that.
196
242
197
243
```csharp
198
244
publicclassAsideTest : ComponentTestFixture
199
245
{
200
-
[Fact(DisplayName="Aside should render child markup content correctly")]
201
-
publicvoidTest002()
246
+
[Fact(DisplayName="Aside should render child markup content correctly")]
247
+
publicvoidTest003()
202
248
{
203
249
// Arrange
204
250
varcontent="<p>I like simple tests and I cannot lie</p>";
@@ -221,7 +267,7 @@ public class AsideTest : ComponentTestFixture
221
267
}
222
268
223
269
[Fact(DisplayName="Aside should render a child component correctly")]
224
-
publicvoidTest003()
270
+
publicvoidTest004()
225
271
{
226
272
// Arrange - set up test data
227
273
varouterAsideHeader="Hello outside";
@@ -255,8 +301,8 @@ public class AsideTest : ComponentTestFixture
255
301
}
256
302
```
257
303
258
-
- In `Test002` above we use the `ChildContent(...)` helper method to create a ChildContent parameter and pass that to the `Aside` component.
259
-
- The overload, `ChildContent<TComponent>(...)`, used in `Test003`, allows us to create a render fragment that will render a component (of type `TComponent`) with the specified parameters.
304
+
- In `Test003` above we use the `ChildContent(...)` helper method to create a ChildContent parameter and pass that to the `Aside` component.
305
+
- The overload, `ChildContent<TComponent>(...)`, used in `Test004`, allows us to create a render fragment that will render a component (of type `TComponent`) with the specified parameters.
260
306
The `ChildContent<TComponent>(...)` has the same parameter options as the `RenderComponent<TComponent>` method has.
261
307
262
308
## Testing components with `EventCallback` parameters
@@ -373,3 +419,157 @@ public class ThemedButtonTest : ComponentTestFixture
373
419
374
420
-`Test002` above uses the `CascadingValue(object value)` helper method to pass an **unnamed** cascading value to the CUT.
375
421
-`Test003` above demonstrates how multiple (named) cascading values can be passed to a component under test.
422
+
423
+
## Testing components that use on `IJsRuntime`
424
+
425
+
It is not uncommon to have components use Blazor's JSInterop functionality to call JavaScript, e.g. after first render.
426
+
427
+
To make it easy to mock calls to JavaScript, the library comes with a `IJsRuntime` mocking helper, that allows you to specify return how JSInterop calls should be handled, and to verify that they have happened.
428
+
429
+
If you have more complex mocking needs, you could look to frameworks like [Moq](https://github.com/Moq).
430
+
431
+
To help us test the Mock JSRuntime, we have the [WikiSearch.razor](../sample/src/Components/WikiSearch.razor) component, which looks like this:
432
+
433
+
```cshtml
434
+
@inject IJSRuntime jsRuntime
435
+
436
+
<p>@searchResult</p>
437
+
438
+
@code {
439
+
string searchResult = string.Empty;
440
+
441
+
// Assumes the following function is available in the DOM
-`Test001` just injects the mock in "Loose" mode. It means it will only returns a `default(TValue)` for calls to `InvokeAsync<TValue>(...)` it receives. This allows us to test components that expects a `IJsRuntime` to be injected, but where the test we want to perform isn't dependent on it providing any specific return value.
514
+
515
+
In "Loose" mode it is still possible to call `VerifyInvoke(identifier)` and assert against the expected invocation.
516
+
517
+
-`Test002` injects and configures the mock in strict mode. That requires us to configure all the expected calls the mock should handle. If it receives a call it has not been configured for, an exception is thrown and the test fails.
518
+
519
+
- The `WaitForNextRender(Action)` helper method is used to block until a (async) render completes, that the action passed to it has triggered.
520
+
In `Test002` we trigger a render by setting the result on the planned invocation, which causes the `await jsRuntime.InvokeAsync<string>("queryWiki", "blazor")` call in the CUT to complete, and the component to trigger a re-render by calling the `StateHasChanged()` method.
521
+
522
+
### Verifying element references passed to InvokeAsync
523
+
524
+
If you want to verify that a element reference (`ElementReference`) passed to a IJsRuntime.InvokeAsync call is references the expected DOM element, you can do so with the `ShouldBeElementReferenceTo()` assert helper.
525
+
526
+
For example, consider the [FocussingInput.razor](../sample/src/Components/FocussingInput.razor) component, which looks like this:
The last line verifies that there was a single argument to the invocation, and via the `ShouldBeElementReferenceTo` checks, that the `<input />` was indeed the referenced element.
0 commit comments