Disposing components
To dispose all components rendered with a BunitContext, use the DisposeComponents method. Calling this method will dispose all rendered components, calling any IDisposable.Dispose or/and IAsyncDisposable.DisposeAsync methods they might have, and remove the components from the render tree, starting with the root components and then walking down the render tree to all the child components.
Disposing rendered components enables testing of logic in Dispose methods, e.g., event handlers, that should be detached to avoid memory leaks.
The following example of this:
var calledTimes = 0;
var cut = Render<DisposableComponent>(parameters => parameters
.Add(p => p.LocationChangedCallback, url => calledTimes++)
);
await DisposeComponentsAsync();
Services.GetRequiredService<NavigationManager>().NavigateTo("newurl");
Assert.Equal(0, calledTimes);
Warning
For IAsyncDisposable (since .net5) relying on WaitForState() or WaitForAssertion() will not work as a disposed component will not trigger a new render cycle.
Disposing components asynchronously
If a component implements IAsyncDisposable, DisposeComponentsAsync can be awaited to wait for all asynchronous DisposeAsync methods. Sometimes interacting with JavaScript in Blazor WebAssembly requires disposing or resetting state in DisposeAsync.
JSInterop.SetupVoid("dispose").SetVoidResult();
Render<AsyncDisposableComponent>();
await DisposeComponentsAsync();
JSInterop.VerifyInvoke("dispose");
To omit this behavior, discard the returned task
_ = DisposeComponentsAsync();
Checking for exceptions
Both Dispose and DisposeAsync can throw exceptions, which can be asserted during tests. When a component under test throws an exception in either Dispose or DisposeAsync, the exception is caught by the renderer and made available via the Renderer.UnhandledException task. The DisposeComponentsAsync method itself will not throw the exception.
This allows for consistent testing of exceptions during disposal, regardless of whether the disposal is synchronous or asynchronous.
The following examples demonstrate how to assert that an exception was thrown during disposal:
Asserting exception in Dispose:
[Fact]
public async Task ShouldCatchExceptionInDispose()
{
Render<ExceptionInDisposeComponent>();
await DisposeComponentsAsync();
var exception = await Renderer.UnhandledException;
Assert.IsType<NotSupportedException>(exception);
Asserting exception in DisposeAsync:
[Fact]
public async Task ShouldCatchExceptionInDisposeAsync()
{
Render<ExceptionInDisposeAsyncComponent>();
await DisposeComponentsAsync();
var exception = await Renderer.UnhandledException;
Assert.IsType<NotSupportedException>(exception);