Skip to content

Commit e02f42a

Browse files
committed
refactor: Remove abstraction around RenderedFragment/RenderedComponent
1 parent d6478b9 commit e02f42a

File tree

50 files changed

+300
-334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+300
-334
lines changed

MIGRATION.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
This document describes the changes that need to be made to migrate from bUnit 1.x to 2.x.
33

44
## Removal of `GetChangesSinceFirstRender` and `GetChangesSinceLastRender` methods
5-
The `GetChangesSinceFirstRender` and `GetChangesSinceLastRender` methods have been removed from `IRenderedComponent<TComponent>`. There is no one-to-one replacement for these methods, but the general idea is to select the HTML in question via `Find` and assert against that.
5+
The `GetChangesSinceFirstRender` and `GetChangesSinceLastRender` methods have been removed from `RenderedComponent<TComponent>`. There is no one-to-one replacement for these methods, but the general idea is to select the HTML in question via `Find` and assert against that.
66

77
Alternatively, the `IRenderFragment` still offers the `OnMarkupUpdated` event, which can be used to assert against the markup after a render.
88

@@ -28,8 +28,8 @@ The `bunit.core` and `bunit.web` packages have been merged into a single `bunit`
2828

2929
## Removal of unneeded abstraction
3030

31-
### `IRenderedComponentBase<TComponent>` and `IRenderedFragmentBase`
32-
`IRenderedComponentBase<TComponent>` and `IRenderedFragmentBase` have been removed. They were used to provide a common base class for `IRenderedComponent<TComponent>` and `IRenderedFragment`, but this is no longer needed (due to the merge of the project). If you used either of these interfaces, you should replace them with `IRenderedComponent<TComponent>` and `IRenderedFragment` respectively.
31+
### `IRenderedComponentBase<TComponent>` and `RenderedFragmentBase`
32+
`IRenderedComponentBase<TComponent>` and `RenderedFragmentBase` have been removed. They were used to provide a common base class for `RenderedComponent<TComponent>` and `RenderedFragment`, but this is no longer needed (due to the merge of the project). If you used either of these interfaces, you should replace them with `RenderedComponent<TComponent>` and `RenderedFragment` respectively.
3333

3434
### `WebTestRender` merged into `TestRender`
3535
The `WebTestRender` class has been merged into the `TestRender` class. If you used `WebTestRender`, you should replace it with `TestRender`.

docs/site/docs/getting-started/writing-tests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Testing Blazor components is a little different from testing regular C# classes:
99

1010
Use **bUnit** to render the component under test, pass in its parameters, inject required services, and access the rendered component instance and the markup it has produced.
1111

12-
Rendering a component happens through bUnit's <xref:Bunit.TestContext>. The result of the rendering is an `IRenderedComponent`, referred to as a "rendered component", that provides access to the component instance and the markup produced by the component.
12+
Rendering a component happens through bUnit's <xref:Bunit.TestContext>. The result of the rendering is an `RenderedComponent`, referred to as a "rendered component", that provides access to the component instance and the markup produced by the component.
1313

1414
## Write tests in `.cs` or `.razor` files
1515

docs/site/docs/interaction/awaiting-async-state.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ A test can fail if a component performs asynchronous renders. This may be due to
99

1010
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.
1111

12-
bUnit comes with two methods that help to deal with this issue: the [`WaitForState()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragment,Func{System.Boolean},System.Nullable{TimeSpan})) method covered on this page, and the [`WaitForAssertion()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragment,Action,System.Nullable{TimeSpan})) method covered on the <xref:async-assertion> page.
12+
bUnit comes with two methods that help to deal with this issue: the [`WaitForState()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.RenderedFragment,Func{System.Boolean},System.Nullable{TimeSpan})) method covered on this page, and the [`WaitForAssertion()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.RenderedFragment,Action,System.Nullable{TimeSpan})) method covered on the <xref:async-assertion> page.
1313

1414
Let's start by taking a look at the `WaitForState` method in more detail.
1515

1616
## Waiting for state using `WaitForState`
1717

18-
The [`WaitForState(Func<Boolean>, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragment,Func{System.Boolean},System.Nullable{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.)
18+
The [`WaitForState(Func<Boolean>, TimeSpan?)`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.RenderedFragment,Func{System.Boolean},System.Nullable{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.)
1919

2020
> [!NOTE]
2121
> The `WaitForState()` method will try the predicate passed to it when the `WaitForState()` method is called, and every time the component under test renders.

docs/site/docs/interaction/dispose-components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The following example of this:
1313
[!code-csharp[](../../../samples/tests/xunit/DisposeComponentsTest.cs#L13-L22)]
1414

1515
> [!WARNING]
16-
> For `IAsyncDisposable` (since .net5) relying on [`WaitForState()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.IRenderedFragment,Func{System.Boolean},System.Nullable{TimeSpan})) or [`WaitForAssertion()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.IRenderedFragment,Action,System.Nullable{TimeSpan})) will not work as a disposed component will not trigger a new render cycle.
16+
> For `IAsyncDisposable` (since .net5) relying on [`WaitForState()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForState(Bunit.RenderedFragment,Func{System.Boolean},System.Nullable{TimeSpan})) or [`WaitForAssertion()`](xref:Bunit.RenderedFragmentWaitForHelperExtensions.WaitForAssertion(Bunit.RenderedFragment,Action,System.Nullable{TimeSpan})) will not work as a disposed component will not trigger a new render cycle.
1717
1818
## Checking for exceptions
1919
`Dispose` as well as `DisposeAsync` can throw exceptions which can be asserted as well. If a component under test throws an exception in `Dispose` the [`DisposeComponents`](xref:Bunit.TestContext.DisposeComponents) will throw the exception to the user code:

docs/site/docs/interaction/trigger-event-handlers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ bUnit comes with event dispatch helper methods that makes it possible to invoke
2323
- [Progress events](xref:Bunit.ProgressEventDispatchExtensions)
2424
- [Touch event](xref:Bunit.TouchEventDispatchExtensions)
2525

26-
To use these, first find the element in the component under test where the event handler is bound. This is usually done with the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method. Next, invoke the event dispatch helper method of choice.
26+
To use these, first find the element in the component under test where the event handler is bound. This is usually done with the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.RenderedFragment,System.String)) method. Next, invoke the event dispatch helper method of choice.
2727

2828
The following section demonstrates how to do this...
2929

@@ -49,7 +49,7 @@ To trigger the `@onclick` `ClickHandler` event handler method in the `<ClickMe>`
4949

5050
This is what happens in the test:
5151

52-
1. In the arrange step of the test, the `<ClickMe>` component is rendered and the `<button>` element is found using the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.IRenderedFragment,System.String)) method.
52+
1. In the arrange step of the test, the `<ClickMe>` component is rendered and the `<button>` element is found using the [`Find(string cssSelector)`](xref:Bunit.RenderedFragmentExtensions.Find(Bunit.RenderedFragment,System.String)) method.
5353
2. The act step of the test is the `<button>`'s click event handler. In this case, the `ClickHandler` event handler method is invoked in three different ways:
5454
- The first and second invocations use the same [`Click`](xref:Bunit.MouseEventDispatchExtensions.Click(AngleSharp.Dom.IElement,System.Int64,System.Double,System.Double,System.Double,System.Double,System.Int64,System.Int64,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.String)) method. It has a number of optional arguments, some of which are passed in the second invocation. If any arguments are provided, they are added to an instance of the `MouseEventArgs` type, which is passed to the event handler if it has it as an argument.
5555
- The last invocation uses the [`Click`](xref:Bunit.MouseEventDispatchExtensions.Click(AngleSharp.Dom.IElement,Microsoft.AspNetCore.Components.Web.MouseEventArgs)) method. This takes an instance of the `MouseEventArgs` type, which is passed to the event handler if it has it as an argument.

docs/site/docs/interaction/trigger-renders.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,37 @@ title: Triggering a render life cycle on a component
55

66
# Triggering a render life cycle on a component
77

8-
To trigger a re-render of a component under test, a reference to it through a <xref:Bunit.IRenderedComponent`1> type is needed. When using the <xref:Bunit.TestContext>'s `RenderComponent<TComponent>()` method, this is the type returned.
8+
To trigger a re-render of a component under test, a reference to it through a <xref:Bunit.RenderedComponent`1> type is needed. When using the <xref:Bunit.TestContext>'s `RenderComponent<TComponent>()` method, this is the type returned.
99

10-
In `.razor` based tests, using the <xref:Bunit.TestContext>'s <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method also returns an <xref:Bunit.IRenderedComponent`1> (as opposed to the <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method which returns the more simple <xref:Bunit.IRenderedFragment>).
10+
In `.razor` based tests, using the <xref:Bunit.TestContext>'s <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method also returns an <xref:Bunit.RenderedComponent`1> (as opposed to the <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method which returns the more simple <xref:Bunit.RenderedFragment>).
1111

12-
If you have a <xref:Bunit.IRenderedFragment> or a <xref:Bunit.IRenderedComponent`1> in a test, but need a child component's <xref:Bunit.IRenderedComponent`1>, then use the `FindComponent<TComponent>()` or the `FindComponents<TComponent>()` methods, which traverse down the render tree and finds rendered components.
12+
If you have a <xref:Bunit.RenderedFragment> or a <xref:Bunit.RenderedComponent`1> in a test, but need a child component's <xref:Bunit.RenderedComponent`1>, then use the `FindComponent<TComponent>()` or the `FindComponents<TComponent>()` methods, which traverse down the render tree and finds rendered components.
1313

14-
With a <xref:Bunit.IRenderedComponent`1>, it is possible to cause the component to render again directly through the [`Render()`](xref:Bunit.TestContext.Render``1(Microsoft.AspNetCore.Components.RenderFragment)) method or one of the [`SetParametersAndRender()`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.IRenderedComponent{``0},System.Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods, or indirectly through the [`InvokeAsync()`](xref:Bunit.IRenderedFragment.Bunit.RenderedFragmentInvokeAsyncExtensions.InvokeAsync(System.Action)) method.
14+
With a <xref:Bunit.RenderedComponent`1>, it is possible to cause the component to render again directly through the [`Render()`](xref:Bunit.TestContext.Render``1(Microsoft.AspNetCore.Components.RenderFragment)) method or one of the [`SetParametersAndRender()`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.RenderedComponent{``0},System.Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods, or indirectly through the [`InvokeAsync()`](xref:Bunit.RenderedFragment.Bunit.RenderedFragmentInvokeAsyncExtensions.InvokeAsync(System.Action)) method.
1515

1616
Let's look at how to use each of these methods to cause a re-render.
1717

1818
## Render
1919

20-
The [`Render()`](xref:Bunit.RenderedComponentRenderExtensions.Render``1(Bunit.IRenderedComponent{``0})) method tells the renderer to re-render the component, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods). To use it, do the following:
20+
The [`Render()`](xref:Bunit.RenderedComponentRenderExtensions.Render``1(Bunit.RenderedComponent{``0})) method tells the renderer to re-render the component, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods). To use it, do the following:
2121

2222
[!code-csharp[](../../../samples/tests/xunit/ReRenderTest.cs?start=16&end=22&highlight=5)]
2323

24-
The highlighted line shows the call to [`Render()`](xref:Bunit.RenderedComponentRenderExtensions.Render``1(Bunit.IRenderedComponent{``0})).
24+
The highlighted line shows the call to [`Render()`](xref:Bunit.RenderedComponentRenderExtensions.Render``1(Bunit.RenderedComponent{``0})).
2525

2626
> [!TIP]
27-
> The number of renders a component has been through can be inspected and verified using the <xref:Bunit.IRenderedFragment.RenderCount> property.
27+
> The number of renders a component has been through can be inspected and verified using the <xref:Bunit.RenderedFragment.RenderCount> property.
2828
2929
## SetParametersAndRender
3030

31-
The [`SetParametersAndRender(...)`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.IRenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods tells the renderer to re-render the component with new parameters, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods), passing the new parameters &mdash; _but only the new parameters_ &mdash; to the `SetParametersAsync()` method. To use it, do the following:
31+
The [`SetParametersAndRender(...)`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.RenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods tells the renderer to re-render the component with new parameters, i.e. go through its life-cycle methods (except for `OnInitialized()` and `OnInitializedAsync()` methods), passing the new parameters &mdash; _but only the new parameters_ &mdash; to the `SetParametersAsync()` method. To use it, do the following:
3232

3333
[!code-csharp[](../../../samples/tests/xunit/ReRenderTest.cs?start=29&end=39&highlight=7-9)]
3434

35-
The highlighted line shows the call to [`SetParametersAndRender()`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.IRenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})), which is also available as a version that takes the zero or more component parameters, e.g. created through the component parameter factory helper methods, if you prefer that method of passing parameters.
35+
The highlighted line shows the call to [`SetParametersAndRender()`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.RenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})), which is also available as a version that takes the zero or more component parameters, e.g. created through the component parameter factory helper methods, if you prefer that method of passing parameters.
3636

3737
> [!NOTE]
38-
> Passing parameters to components through the [`SetParametersAndRender(...)`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.IRenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods is identical to doing it with the `RenderComponent<TComponent>(...)` methods, described in detail on the <xref:passing-parameters-to-components> page.
38+
> Passing parameters to components through the [`SetParametersAndRender(...)`](xref:Bunit.RenderedComponentRenderExtensions.SetParametersAndRender``1(Bunit.RenderedComponent{``0},Action{Bunit.ComponentParameterCollectionBuilder{``0}})) methods is identical to doing it with the `RenderComponent<TComponent>(...)` methods, described in detail on the <xref:passing-parameters-to-components> page.
3939
4040
## InvokeAsync
4141

docs/site/docs/providing-input/passing-parameters-to-components.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ It is possible to nest a component under tests inside other components, if that
377377

378378
[!code-csharp[NestedComponentTest](../../../samples/tests/xunit/NestedComponentTest.cs#L11-L23)]
379379

380-
The example renders the `<HelloWorld>` component inside the `<Wrapper>` component. What is special in both cases is the use of the `FindComponent<HelloWorld>()` that returns a `IRenderedComponent<HelloWorld>`. This is needed because the `RenderComponent<Wrapper>` method call returns an `IRenderedComponent<Wrapper>` instance, that provides access to the instance of the `<Wrapper>` component, but not the `<HelloWorld>`-component instance.
380+
The example renders the `<HelloWorld>` component inside the `<Wrapper>` component. What is special in both cases is the use of the `FindComponent<HelloWorld>()` that returns a `RenderedComponent<HelloWorld>`. This is needed because the `RenderComponent<Wrapper>` method call returns an `RenderedComponent<Wrapper>` instance, that provides access to the instance of the `<Wrapper>` component, but not the `<HelloWorld>`-component instance.
381381

382382
# [Razor test code](#tab/razor)
383383

@@ -444,7 +444,7 @@ The solution is to inherit from bUnits `TestContext` instead, i.e.:
444444

445445
## Limitations of rendering a `RenderFragment` inside a test
446446

447-
When rendering a `RenderFragment` using the <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method, the created <xref:Bunit.IRenderedFragment> is static. This means that it will not re-render even if events are triggered.
447+
When rendering a `RenderFragment` using the <xref:Bunit.TestContext.Render(Microsoft.AspNetCore.Components.RenderFragment)> method, the created <xref:Bunit.RenderedFragment> is static. This means that it will not re-render even if events are triggered.
448448
```razor
449449
@inherits TestContext
450450

docs/site/docs/providing-input/substituting-components.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void Foo_Doesnt_Have_A_Bar_But_Stub()
5050
ComponentFactories.AddStub<Bar>();
5151

5252
// Render the component under test.
53-
IRenderedFragment cut = Render(@<Foo />);
53+
RenderedFragment cut = Render(@<Foo />);
5454

5555
// Verify that the Bar component has
5656
// been substituted in the render tree.
@@ -97,10 +97,10 @@ public void Foo_Doesnt_Have_A_Bar_But_Stub()
9797
{
9898
ComponentFactories.AddStub<Bar>();
9999

100-
IRenderedFragment cut = Render(@<Foo />);
100+
RenderedFragment cut = Render(@<Foo />);
101101

102102
// Find the stubbed component in the render tree
103-
IRenderedComponent<Stub<Bar>> barStub = cut.FindComponent<Stub<Bar>>();
103+
RenderedComponent<Stub<Bar>> barStub = cut.FindComponent<Stub<Bar>>();
104104

105105
// Access parameters passed to it through the stubbed components
106106
// Parameters property, using the selector to pick out the parameter.
@@ -177,11 +177,11 @@ public void Foo_Doesnt_Have_A_Bar_But_Mock()
177177
ComponentFactories.Add<Bar>(barMock.Object);
178178

179179
// Render the component under test
180-
IRenderedFragment cut = Render(@<Foo />);
180+
RenderedFragment cut = Render(@<Foo />);
181181

182182
// Verify that the Bar component has
183183
// been substituted in the render tree
184-
IRenderedComponent<Bar> bar = cut.FindComponent<Bar>();
184+
RenderedComponent<Bar> bar = cut.FindComponent<Bar>();
185185
Assert.Same(barMock.Object, bar.Instance);
186186
}
187187
```
@@ -199,11 +199,11 @@ public void Foo_Doesnt_Have_A_Bar_But_Mock()
199199
ComponentFactories.Add<Bar>(barMock);
200200

201201
// Render the component under test
202-
IRenderedFragment cut = Render(@<Foo />);
202+
RenderedFragment cut = Render(@<Foo />);
203203

204204
// Verify that the Bar component has
205205
// been substituted in the render tree
206-
IRenderedComponent<Bar> bar = cut.FindComponent<Bar>();
206+
RenderedComponent<Bar> bar = cut.FindComponent<Bar>();
207207
Assert.Same(barMock, bar.Instance);
208208
}
209209
```

0 commit comments

Comments
 (0)