Skip to content

Commit 78ec7fe

Browse files
committed
docs: mocking ijsruntime
1 parent f18a4e8 commit 78ec7fe

File tree

2 files changed

+72
-9
lines changed

2 files changed

+72
-9
lines changed
Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,78 @@
11
---
22
uid: mocking-ijsruntime
3-
title: Mocking Blazor's IJsRuntime
3+
title: Mocking Blazor's IJSRuntime
44
---
55

6-
# Mocking Blazor's `IJsRuntime`
6+
# Mocking Blazor's `IJSRuntime`
77

8-
This page is on the todo list.
8+
It is common for Blazor components to use `IJSRuntime` to call JavaScript, and since bUnit does not run JavaScript, mocking `IJSRuntime` is needed for components that uses it. In that regard, `IJSRuntime` is no different than other services that a component might depend on.
99

10-
To see examples of how to mock the JsRuntime, go to the [C# test examples](/docs/csharp-test-examples.html#testing-components-that-use-on-ijsruntime) page.
10+
bUnit comes a tailor built mock of `IJSRuntime`, that allows you to specify how JavaScript interop calls should be handled, what values they should return, and to verify that they have happened.
1111

12-
- TODO: https://github.com/egil/bunit/issues/67
12+
If you have more complex mocking needs, or you prefer to use the same mocking framework for all mocking in your tests to keep things consistent, general purpose mocking frameworks like [Moq](https://github.com/Moq), [JustMock Lite](https://github.com/telerik/JustMockLite), or [NSubstitute](https://nsubstitute.github.io/) all works nicely with bUnit.
13+
14+
The following sections shows how to use the built-in mock of `IJSRuntime`.
15+
16+
## Registering the mock `IJSRuntime`
17+
18+
A mock of `IJSRuntime` must be added to the `Services` collection, just like other services that a component under test requires. This is done like this:
19+
20+
```csharp
21+
using var ctx = new TestContext();
22+
var mockJS = ctx.Services.AddMockJSRuntime();
23+
```
24+
25+
Calling `AddMockJSRuntime()` returns a <xref:Bunit.TestDoubles.JSInterop.MockJSRuntimeInvokeHandler>, which is used to set up expected calls and verify invocations.
26+
27+
### Strict vs loose mode
28+
29+
The `AddMockJSRuntime()` method takes an optional <xref:Bunit.TestDoubles.JSInterop.JSRuntimeMockMode> parameter as input, which defaults to `Loose`, if not provided.
30+
31+
- **Loose** mode configures the mock to just return the default value when it receives an invocation that has not been explicitly set up, e.g. if a component calls `InvokeAsync<int>(...)` the mock will simply return `default(int)` back to it immediately.
32+
- **Strict** mode configures the mock to throw an exception if it is invoked with a method call it has _not_ been set up to handle explicitly. This is useful if you want to ensure that a component only performs a specific set of `IJSRuntime` invocations.
33+
34+
To set the mock to strict mode, do the following:
35+
36+
```csharp
37+
using var ctx = new TestContext();
38+
var mockJS = ctx.Services.AddMockJSRuntime(JSRuntimeMockMode.Strict);
39+
```
40+
41+
## Setting up invocations
42+
43+
Use the `Setup<TResult>(...)` and `SetupVoid(...)` methods to configure the mock to handle calls from the matching `InvokeAsync<TResult>(...)` and `InvokeVoidAsync(...)` methods on `IJSRuntime`.
44+
45+
When an invocation is set up through the `Setup<TResult>(...)` and `SetupVoid(...)` methods, a `JSRuntimePlannedInvocation<TResult>` object is returned. This can be used to set a result or an exception, to emulate what can happen during a JavaScript interop call in Blazor.
46+
47+
Here are two examples:
48+
49+
```csharp
50+
using var ctx = new TestContext();
51+
var mockJS = ctx.Services.AddMockJSRuntime();
52+
53+
// Set up an invocation and specify the result value immidiately
54+
mockJS.Setup<string>("getPageTitle").SetResult("bUnit is awesome");
55+
56+
// Set up an invocation without specifying the result
57+
var plannedInvocation = mockJS.SetupVoid("startAnimation");
58+
59+
// ... other test code
60+
61+
// Later in the test, mark the invocation as completed.
62+
// SetResult() is not used in this case since InvokeVoidAsync
63+
// only completes or throws, it doesnt return a value.
64+
// Any calls to InvokeVoidAsync(...) up till this point will
65+
// have received an incompleted Task which the component
66+
// is likely awaiting until the call to SetCompleted() below.
67+
plannedInvocation.SetCompleted();
68+
```
69+
70+
## Verifying invocations
71+
72+
All calls to the `InvokeAsync<TResult>(...)` and `InvokeVoidAsync(...)` methods on the mock are stored in its `Invocations` list, which can be inspected and asserted against. In addition to this, all planned invocations has their own `Invocations` list, which only contains their invocations.
73+
74+
Invocations are represented by the `JSRuntimeInvocation` type, which has three properties of interest when verifying an invocation happened as expected:
75+
76+
- `Identifier` - the name of the function name/identifier passed to the invoke method.
77+
- `Arguments` - a list of arguments passed to the invoke method.
78+
- `CancellationToken` - the cancellation token passed to the invoke method (if any).

docs/site/docs/toc.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323

2424
# [Test Doubles](xref:test-doubles)
2525
## [Faking Authorization](xref:faking-auth)
26-
## [Mocking HttpClient](xref:mocking-httpclient)
27-
## [Mocking IJsRuntime](xref:mocking-ijsruntime)
28-
## [Mocking Localization](xref:mocking-localizer)
29-
## [Mocking NavigationManager](xref:mocking-navigation-manager)
26+
## [Mocking IJSRuntime](xref:mocking-ijsruntime)
3027

3128
# [Miscellaneous testing tips](xref:misc-test-tips)
3229
# [External Resources](xref:external-resources)

0 commit comments

Comments
 (0)