Skip to content

Commit 36ab287

Browse files
committed
Merge branch 'dev'
2 parents 955c1af + 3787369 commit 36ab287

File tree

221 files changed

+3838
-1235
lines changed

Some content is hidden

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

221 files changed

+3838
-1235
lines changed

.github/workflows/CI.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
run: dotnet pack template/ -c Release -o ${GITHUB_WORKSPACE}/template -p:version=$VERSION -p:PackageVersion=$VERSION
4242
- name: Verifying template
4343
run: |
44-
dotnet new --install ${GITHUB_WORKSPACE}/template/Razor.Components.Testing.Library.Template.$VERSION.nupkg
45-
dotnet new razortest -o ${GITHUB_WORKSPACE}/Test
44+
dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg
45+
dotnet new bunit -o ${GITHUB_WORKSPACE}/Test
4646
dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib
4747
dotnet test ${GITHUB_WORKSPACE}/Test

.github/workflows/nuget-pack-push.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ jobs:
4040
run: dotnet pack template/ -c Release -o ${GITHUB_WORKSPACE}/template -p:version=$VERSION -p:PackageVersion=$VERSION
4141
- name: Verifying template
4242
run: |
43-
dotnet new --install ${GITHUB_WORKSPACE}/template/Razor.Components.Testing.Library.Template.$VERSION.nupkg
44-
dotnet new razortest -o ${GITHUB_WORKSPACE}/Test
43+
dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg
44+
dotnet new bunit -o ${GITHUB_WORKSPACE}/Test
4545
dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib
4646
dotnet test ${GITHUB_WORKSPACE}/Test
4747
- name: Push packages to NuGet.org

CHANGELOG.md

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# Changelog
2+
All notable changes to **bUnit** will be documented in this file. The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
3+
4+
## [Unreleased]
5+
This release includes a **name change from Blazor Components Testing Library to bUnit**. It also brings along two extra helper methods for working with asynchronously rendering components during testing, and a bunch of internal optimizations and tweaks to the code.
6+
7+
*Why change the name?* Naming is hard, and I initial chose a very product-namy name, that quite clearly stated what the library was for. However, the name isn't very searchable, since it just contains generic keywords, plus, bUnit is just much cooler. It also gave me the opportunity to remove my name from all the namespaces and simplify those.
8+
9+
### NuGet
10+
The latest version of the library is availble on NuGet:
11+
12+
| | Type | Link |
13+
| ------------- | ----- | ---- |
14+
| [![Nuget](https://img.shields.io/nuget/dt/bunit?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit/) | Library | [https://www.nuget.org/packages/bunit/](https://www.nuget.org/packages/bunit/) |
15+
| [![Nuget](https://img.shields.io/nuget/dt/bunit.template?logo=nuget&style=flat-square)](https://www.nuget.org/packages/bunit.template/) | Template | [https://www.nuget.org/packages/bunit.template/](https://www.nuget.org/packages/bunit.template/) |
16+
17+
### Added
18+
- **`WaitForState(Func<bool> statePredicate, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.**
19+
This method will wait (block) until the provided statePredicate returns true, or the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the statePredicate is evaluated.
20+
21+
You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue.
22+
23+
The following example tests the `DelayedRenderOnClick.razor` component:
24+
25+
```cshtml
26+
// DelayedRenderOnClick.razor
27+
<p>Times Clicked: @TimesClicked</p>
28+
<button @onclick="ClickCounter">Trigger Render</button>
29+
@code
30+
{
31+
public int TimesClicked { get; private set; }
32+
33+
async Task ClickCounter()
34+
{
35+
await Task.Delay(1); // wait 1 millisecond
36+
TimesClicked += 1;
37+
}
38+
}
39+
```
40+
41+
This is a test that uses `WaitForState` to wait until the component under test has a desired state, before the test continues:
42+
43+
```csharp
44+
[Fact]
45+
public void WaitForStateExample()
46+
{
47+
// Arrange
48+
var cut = RenderComponent<DelayedRenderOnClick>();
49+
50+
// Act
51+
cut.Find("button").Click();
52+
cut.WaitForState(() => cut.Instance.TimesClicked == 1);
53+
54+
// Assert
55+
cut.Find("p").TextContent.ShouldBe("Times Clicked: 1");
56+
}
57+
```
58+
59+
- **`WaitForAssertion(Action assertion, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.**
60+
This method will wait (block) until the provided assertion method passes, i.e. runs without throwing an assert exception, or until the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the assertion is attempted.
61+
62+
You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue.
63+
64+
This is a test that tests the `DelayedRenderOnClick.razor` listed above, and that uses `WaitForAssertion` to attempt the assertion each time the component under test renders:
65+
66+
```csharp
67+
[Fact]
68+
public void WaitForAssertionExample()
69+
{
70+
// Arrange
71+
var cut = RenderComponent<DelayedRenderOnClick>();
72+
73+
// Act
74+
cut.Find("button").Click();
75+
76+
// Assert
77+
cut.WaitForAssertion(
78+
() => cut.Find("p").TextContent.ShouldBe("Times Clicked: 1")
79+
);
80+
}
81+
```
82+
83+
- **Added support for capturing log statements from the renderer and components under test into the test output.**
84+
To enable this, add a constructor to your test classes that takes the `ITestOutputHelper` as input, then in the constructor call `Services.AddXunitLogger` and pass the `ITestOutputHelper` to it, e.g.:
85+
86+
```csharp
87+
// ComponentTest.cs
88+
public class ComponentTest : ComponentTestFixture
89+
{
90+
public ComponentTest(ITestOutputHelper output)
91+
{
92+
Services.AddXunitLogger(output, minimumLogLevel: LogLevel.Debug);
93+
}
94+
95+
[Fact]
96+
public void Test1() ...
97+
}
98+
```
99+
100+
For Razor and Snapshot tests, the logger can be added almost the same way. The big difference is that it must be added during *Setup*, e.g.:
101+
102+
```cshtml
103+
// RazorComponentTest.razor
104+
<Fixture Setup="Setup" ...>
105+
...
106+
</Fixture>
107+
@code {
108+
private ITestOutputHelper _output;
109+
110+
public RazorComponentTest(ITestOutputHelper output)
111+
{
112+
_output = output;
113+
}
114+
115+
void Setup()
116+
{
117+
Services.AddXunitLogger(_output, minimumLogLevel: LogLevel.Debug);
118+
}
119+
}
120+
```
121+
122+
- **Added simpler `Template` helper method**
123+
To make it easier to test components with `RenderFragment<T>` parameters (template components) in C# based tests, a new `Template<TValue>(string name, Func<TValue, string> markupFactory)` helper methods have been added. It allows you to create a mock template that uses the `markupFactory` to create the rendered markup from the template.
124+
125+
This is an example of testing the `SimpleWithTemplate.razor`, which looks like this:
126+
127+
```cshtml
128+
@typeparam T
129+
@foreach (var d in Data)
130+
{
131+
@Template(d);
132+
}
133+
@code
134+
{
135+
[Parameter] public RenderFragment<T> Template { get; set; }
136+
[Parameter] public IReadOnlyList<T> Data { get; set; } = Array.Empty<T>();
137+
}
138+
```
139+
140+
And the test code:
141+
142+
```csharp
143+
var cut = RenderComponent<SimpleWithTemplate<int>>(
144+
("Data", new int[] { 1, 2 }),
145+
Template<int>("Template", num => $"<p>{num}</p>")
146+
);
147+
148+
cut.MarkupMatches("<p>1</p><p>2</p>");
149+
```
150+
151+
Using the more general `Template` helper methods, you need to write the `RenderTreeBuilder` logic yourself, e.g.:
152+
153+
```csharp
154+
var cut = RenderComponent<SimpleWithTemplate<int>>(
155+
("Data", new int[] { 1, 2 }),
156+
Template<int>("Template", num => builder => builder.AddMarkupContent(0, $"<p>{num}</p>"))
157+
);
158+
```
159+
160+
- **Added logging to TestRenderer.** To make it easier to understand the rendering life-cycle during a test, the `TestRenderer` will now log when ever it dispatches an event or renders a component (the log statements can be access by capturing debug logs in the test results, as mentioned above).
161+
162+
- **Added some of the Blazor frameworks end-2-end tests.** To get better test coverage of the many rendering scenarios supported by Blazor, the [ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) tests from the Blazor frameworks test suite has been converted from a Selenium to a bUnit. The testing style is very similar, so few changes was necessary to port the tests. The two test classes are here, if you want to compare:
163+
164+
- [bUnit's ComponentRenderingTest.cs](/master/tests/BlazorE2E/ComponentRenderingTest.cs)
165+
- [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs)
166+
167+
### Changed
168+
- **Namespaces is now `Bunit`**
169+
The namespaces have changed from `Egil.RazorComponents.Testing.Library.*` to simply `Bunit` for the library, and `Bunit.Mocking.JSInterop` for the JSInterop mocking support.
170+
171+
- **Auto-refreshing `IElement`s returned from `Find()`**
172+
`IRenderedFragment.Find(string cssSelector)` now returns a `IElement`, which internally will refresh itself, whenever the rendered fragment it was found in, changes. This means you can now search for an element once in your test and assign it to a variable, and then continue to assert against the same instance, even after triggering renders of the component under test.
173+
174+
For example, instead of having `cut.Find("p")` in multiple places in the same test, you can do `var p = cut.Find("p")` once, and the use the variable `p` all the places you would otherwise have the `Find(...)` statement.
175+
176+
- **Refreshable element collection returned from `FindAll`.**
177+
The `FindAll` query method on `IRenderedFragment` now returns a new type, the `IRefreshableElementCollection<IElement>` type, and the method also takes a second optional argument now, `bool enableAutoRefresh = false`.
178+
179+
The `IRefreshableElementCollection` is a special collection type that can rerun the query to refresh its the collection of elements that are found by the CSS selector. This can either be done manually by calling the `Refresh()` method, or automatically whenever the rendered fragment renders and has changes, by setting the property `EnableAutoRefresh` to `true` (default set to `false`).
180+
181+
Here are two example tests, that both test the following `ClickAddsLi.razor` component:
182+
183+
```cshtml
184+
<ul>
185+
@foreach (var x in Enumerable.Range(0, Counter))
186+
{
187+
<li>@x</li>
188+
}
189+
</ul>
190+
<button @onclick="() => Counter++"></button>
191+
@code {
192+
public int Counter { get; set; } = 0;
193+
}
194+
```
195+
196+
The first tests uses auto refresh, set through the optional parameter `enableAutoRefresh` passed to FindAll:
197+
198+
```csharp
199+
public void AutoRefreshQueriesForNewElementsAutomatically()
200+
{
201+
var cut = RenderComponent<ClickAddsLi>();
202+
var liElements = cut.FindAll("li", enableAutoRefresh: true);
203+
liElements.Count.ShouldBe(0);
204+
205+
cut.Find("button").Click();
206+
207+
liElements.Count.ShouldBe(1);
208+
}
209+
```
210+
211+
The second test refreshes the collection manually through the `Refresh()` method on the collection:
212+
213+
```csharp
214+
public void RefreshQueriesForNewElements()
215+
{
216+
var cut = RenderComponent<ClickAddsLi>();
217+
var liElements = cut.FindAll("li");
218+
liElements.Count.ShouldBe(0);
219+
220+
cut.Find("button").Click();
221+
222+
liElements.Refresh(); // Refresh the collection
223+
liElements.Count.ShouldBe(1);
224+
}
225+
```
226+
227+
- **Custom exception when event handler is missing.** Attempting to triggering a event handler on an element which does not have an handler attached now throws a `MissingEventHandlerException` exception, instead of an `ArgumentException`.
228+
229+
### Deprecated
230+
- **`WaitForNextRender` has been deprecated (marked as obsolete)**, since the added `WaitForState` and `WaitForAssertion` provide a much better foundation to build stable tests on. The plan is to remove completely from the library with the final 1.0.0 release.
231+
232+
### Removed
233+
- **`AddMockHttp` and related helper methods have been removed.**
234+
The mocking of HTTPClient, supported through the [mockhttp](https://github.com/richardszalay/mockhttp) library, has been removed from the library. This was done because the library really shouldn't have a dependency on a 3. party mocking library. It adds maintenance overhead and uneeded dependencies to it.
235+
236+
If you are using mockhttp, you can easily add again to your testing project. See [TODO Guide to mocking HttpClient](#) in the docs to learn how.
237+
238+
### Fixed
239+
- **Wrong casing on keyboard event dispatch helpers.**
240+
The helper methods for the keyboard events was not probably cased, so that has been updated. E.g. from `Keypress(...)` to `KeyPress(...)`.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
[![Issues Open](https://img.shields.io/github/issues/egil/razor-components-testing-library.svg?style=flat-square&logo=github&style=flat-square)](https://github.com/egil/razor-components-testing-library/issues)
55
[![Gitter](https://img.shields.io/gitter/room/razor-components-testing-library/community?logo=gitter&style=flat-square)](https://gitter.im/razor-components-testing-library/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
66

7-
# Blazor Components Testing Library
7+
# bUnit
88

9-
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 easily interact with and inspect components, trigger event handlers, provide cascading values, inject services, mock IJsRuntime, and perform snapshot testing.
9+
**bUnit**, previously known as **Blazor Components Testing Library**, 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 easily interact with and inspect components, trigger event handlers, provide cascading values, inject services, mock IJsRuntime, and perform snapshot testing.
1010

1111
This library's goal is to make it easy to write _comprehensive, stable unit tests_ for Blazor Components/Razor Components. To see how, head to the Wiki pages:
1212

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A5D7B605-02D8-468C-9BDF-864CF93B12F9}"
77
ProjectSection(SolutionItems) = preProject
88
.editorconfig = .editorconfig
9+
CHANGELOG.md = CHANGELOG.md
910
Directory.Build.props = Directory.Build.props
1011
LICENSE = LICENSE
1112
README.md = README.md
@@ -15,11 +16,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E006E9A4-F55
1516
EndProject
1617
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C929375E-BD70-4B78-88C1-BDD1623C3365}"
1718
EndProject
18-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Egil.RazorComponents.Testing.Library", "src\Egil.RazorComponents.Testing.Library.csproj", "{AA96790B-67C9-4141-ACDB-037C8DC092EC}"
19+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit", "src\bunit.csproj", "{AA96790B-67C9-4141-ACDB-037C8DC092EC}"
1920
EndProject
20-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Egil.RazorComponents.Testing.Library.Tests", "tests\Egil.RazorComponents.Testing.Library.Tests.csproj", "{04E0142A-33CC-4E30-B903-F1370D94AD8C}"
21+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.tests", "tests\bunit.tests.csproj", "{04E0142A-33CC-4E30-B903-F1370D94AD8C}"
2122
EndProject
22-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Egil.Razor.Components.Testing.Library.Template", "template\Egil.Razor.Components.Testing.Library.Template.csproj", "{FB46378D-BFB8-4C72-9CA3-0407D4665218}"
23+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit.template", "template\bunit.template.csproj", "{FB46378D-BFB8-4C72-9CA3-0407D4665218}"
2324
EndProject
2425
Global
2526
GlobalSection(SolutionConfigurationPlatforms) = preSolution

sample/SampleApp.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "src\SampleApp.
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp.Tests", "tests\SampleApp.Tests.csproj", "{04F6D258-F69C-4BB5-87C5-3813C3CE33D8}"
99
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bunit", "..\src\bunit.csproj", "{CCD48527-4741-446F-8D99-E8C1F8A31336}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
2123
{04F6D258-F69C-4BB5-87C5-3813C3CE33D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{04F6D258-F69C-4BB5-87C5-3813C3CE33D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{04F6D258-F69C-4BB5-87C5-3813C3CE33D8}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{CCD48527-4741-446F-8D99-E8C1F8A31336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{CCD48527-4741-446F-8D99-E8C1F8A31336}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{CCD48527-4741-446F-8D99-E8C1F8A31336}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{CCD48527-4741-446F-8D99-E8C1F8A31336}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE

sample/src/Components/DismissingEventArgs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Text;
55
using System.Threading.Tasks;
66

7-
namespace Egil.RazorComponents.Testing.SampleApp.Components
7+
namespace Bunit.SampleApp.Components
88
{
99
public class DismissingEventArgs
1010
{

sample/src/Components/ThemeInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Text;
55
using System.Threading.Tasks;
66

7-
namespace Egil.RazorComponents.Testing.SampleApp.Components
7+
namespace Bunit.SampleApp.Components
88
{
99
public class ThemeInfo
1010
{

sample/src/Data/ITodoService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Collections.Generic;
22
using System.Threading.Tasks;
33

4-
namespace Egil.RazorComponents.Testing.SampleApp.Data
4+
namespace Bunit.SampleApp.Data
55
{
66
public interface ITodoService
77
{

sample/src/Data/IWeatherForecastService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Threading.Tasks;
33

4-
namespace Egil.RazorComponents.Testing.SampleApp.Data
4+
namespace Bunit.SampleApp.Data
55
{
66
public interface IWeatherForecastService
77
{

0 commit comments

Comments
 (0)