Skip to content

Commit a557ac2

Browse files
committed
Docs: inject services
1 parent f69abb5 commit a557ac2

File tree

8 files changed

+171
-2
lines changed

8 files changed

+171
-2
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
5+
namespace Bunit.Docs.Samples
6+
{
7+
public interface IWeatherForecastService
8+
{
9+
Task<WeatherForecast[]> GetForecastAsync(DateTime startDate);
10+
}
11+
12+
public class WeatherForecast
13+
{
14+
public DateTime Date { get; set; }
15+
16+
public int TemperatureC { get; set; }
17+
18+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
19+
20+
public string Summary { get; set; }
21+
}
22+
23+
public class WeatherForecastService : IWeatherForecastService
24+
{
25+
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
26+
=> Task.FromResult(Array.Empty<WeatherForecast>());
27+
}
28+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@code
2+
{
3+
[Parameter] public IEnumerable<WeatherForecast> Forecasts { get; set; }
4+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@inject IWeatherForecastService ForecastService
2+
3+
<h1>Weather forecast</h1>
4+
5+
<p>This component demonstrates fetching data from a service.</p>
6+
7+
@if (Forecasts is null)
8+
{
9+
<p><em>Loading...</em></p>
10+
}
11+
else
12+
{
13+
<WeatherForecastTabel Forecasts=@Forecasts />
14+
}
15+
16+
@code
17+
{
18+
public WeatherForecast[] Forecasts { get; private set; }
19+
20+
protected override async Task OnInitializedAsync()
21+
{
22+
Forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
23+
}
24+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@inherits TestComponentBase
2+
3+
@code
4+
{
5+
void RegisterWeatherForecastServices(SnapshotTest test)
6+
{
7+
test.Services.AddSingleton<IWeatherForecastService>(new WeatherForecastService());
8+
}
9+
}
10+
11+
<SnapshotTest Setup="RegisterWeatherForecastServices">
12+
<TestInput>
13+
<WeatherForecasts />
14+
</TestInput>
15+
<ExpectedOutput>...</ExpectedOutput>
16+
</Fixture>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@inherits TestComponentBase
2+
3+
<Fixture Setup="RegisterWeatherForecastServices"
4+
Test="VerifyServiceInjectedCorrectly">
5+
<ComponentUnderTest>
6+
<WeatherForecasts />
7+
</ComponentUnderTest>
8+
@code
9+
{
10+
void RegisterWeatherForecastServices(Fixture fixture)
11+
{
12+
fixture.Services.AddSingleton<IWeatherForecastService>(new WeatherForecastService());
13+
}
14+
15+
void VerifyServiceInjectedCorrectly(Fixture fixture)
16+
{
17+
// Act - get component under test
18+
var cut = fixture.GetComponentUnderTest<WeatherForecasts>();
19+
20+
// Assert
21+
Assert.NotNull(cut.Instance.Forecasts);
22+
}
23+
}
24+
</Fixture>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Xunit;
2+
using Bunit;
3+
using System.Collections.Generic;
4+
using Microsoft.AspNetCore.Components;
5+
using Microsoft.AspNetCore.Components.Web;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
using static Bunit.ComponentParameterFactory;
9+
10+
namespace Bunit.Docs.Samples
11+
{
12+
public class WeatherForecastsTest
13+
{
14+
[Fact]
15+
public void ServicesIsInjectedCorrectly()
16+
{
17+
using var ctx = new TestContext();
18+
19+
// Register services
20+
ctx.Services.AddSingleton<IWeatherForecastService>(new WeatherForecastService());
21+
22+
// RenderComponent will inject the service in the WeatherForecasts component
23+
// when it is instantiated and rendered.
24+
var cut = ctx.RenderComponent<WeatherForecasts>();
25+
26+
// Assert that service is injected
27+
Assert.NotNull(cut.Instance.Forecasts);
28+
}
29+
}
30+
}

docs/site/docs/providing-input/inject-services-into-components.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,47 @@ title: Injecting Services into Components Under Test
55

66
# Injecting Services into Components Under Test
77

8-
TODO!
8+
It is common that components under test has a dependency on services, injected into them through the `@inject IMyService MyService` syntax in .razor files, or the `[Inject] private IMyService MyService { get; set; }` syntax in .cs files.
9+
10+
This is supported in bUnit through the `Services` collection on the `ITestContext` types that is used in both C# and Razor based tests. It allows services to be registered, how it is done in production code in `Startup.cs` in Blazor Server projects and in `Program.cs` in Blazor Wasm projects.
11+
12+
In bUnit, you register the services in the `Services` collection _before_ you render a component under test.
13+
14+
The following sections shows how to do this in C# and Razor based tests. The examples will test the `<WeatherForecasts>` component listed below, that depends on the `IWeatherForecastService` service, injected in line 1:
15+
16+
[!code-html[WeatherForecasts.razor](../../../samples/components/WeatherForecasts.razor?highlight=1)]
17+
18+
## Injecting Services in C# Based Tests
19+
20+
Here is a C# based test that registers the `IWeatherForecastService` in the `Services` collection, which is needed by the `<WeatherForecasts>` component listed above.
21+
22+
[!code-csharp[WeatherForecastsTest.cs](../../../samples/tests/xunit/WeatherForecastsTest.cs?start=17&end=27&highlight=4)]
23+
24+
The highlighted line shows how the `IWeatherForecastService` is registered in the test context's `Services` collection, which is just a standard [`IServiceCollection`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection), using the standard .NET Core DI services method, [`AddSingleton`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=dotnet-plat-ext-3.1#Microsoft_Extensions_DependencyInjection_ServiceCollectionServiceExtensions_AddSingleton__1_Microsoft_Extensions_DependencyInjection_IServiceCollection___0_).
25+
26+
> [!NOTE]
27+
> The `AddSingleton()` method is only available on the `Services` collection if you import the `Microsoft.Extensions.DependencyInjection` type.
28+
29+
## Injecting Services in Razor Based Tests
30+
31+
Here is a Razor based test that registers the `IWeatherForecastService` in the `Services` collection during the `Setup` methods, which is needed by the `<WeatherForecasts>` component listed above.
32+
33+
[!code-html[WeatherForecastsTest.razor](../../../samples/tests/razor/WeatherForecastsTest.razor?highlight=10-13)]
34+
35+
The highlighted line shows how the `IWeatherForecastService` is registered, using the standard .NET Core DI services method, [`AddSingleton`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=dotnet-plat-ext-3.1#Microsoft_Extensions_DependencyInjection_ServiceCollectionServiceExtensions_AddSingleton__1_Microsoft_Extensions_DependencyInjection_IServiceCollection___0_).
36+
37+
This can either be done via the `Fixture`'s `Setup` method as in this example, if you want to separate the service registration from the test method, or it can be done in the test method _before_ calling `GetComponentUnderTest()`.
38+
39+
In the following example shows how to do this with `<SnapshotTest>` tests:
40+
41+
[!code-html[WeatherForecastsTest.razor](../../../samples/tests/razor/WeatherForecastsSnapshotTest.html?highlight=5-8)]
42+
43+
> [!TIP]
44+
> If multiple Razor tests share the same setup logic, they can share the same dedicated setup method as well.
45+
46+
> [!NOTE]
47+
> The `AddSingleton()` method is only available on the `Services` collection if you import the `Microsoft.Extensions.DependencyInjection` type.
48+
49+
## Further Reading
50+
51+
A closely related topic is mocking. To learn more about mocking in bUnit, go to the <xref:mocking> page.

docs/site/docs/toc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
# [Providing Input](xref:providing-input)
99
## [Parameters and Cascading Values](xref:passing-parameters-to-components)
10-
## [Inject Services](xref:inject-services-into-components)
10+
## [Inject Services into Components](xref:inject-services-into-components)
1111
## [Configure 3rd Party Libraries](xref:configure-3rd-party-libs)
1212

1313
# [Interaction](xref:interaction)

0 commit comments

Comments
 (0)