Skip to content

Commit c8a2fd3

Browse files
joro550egil
andauthored
Added additional placeholder services for NavigationManager, HttpClient, and IStringLocalizer (#223)
* Update affix.tmpl.partial * Adding placeholder navigation manager Adding placeholder navigation manager that throws a helpful exception to prompt users to go look at the docs * Implementing placeholder httpclient Implementing placeholder httpclient that throws exceptions that prompts users to read the documentation * Moving exception to it's own file * Adding placeholder log factory Adding placeholder log factory that throws exceptions to prompt users to create a mock * Added placeholder string localizer Added placeholder string localizer that throws exceptions that prompt the user to look at documentation * Fixing merge conflict * Working on warnings * Docs: added httpclient mocking guide to docs * Code review feedback * Adding xml comment to fix build * Updated changelog.md, moved all test doubles into the same namespace to avoid users having to import many namespaces, cleaned code a little * Update template with new test doubles namespace Co-authored-by: Egil Hansen <egil@assimilated.dk>
1 parent 6fce200 commit c8a2fd3

46 files changed

Lines changed: 395 additions & 40 deletions

File tree

Some content is hidden

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

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ List of new features.
1111

1212
- Two new overloads to the `RenderFragment()` and `ChildContent()` component parameter factory methods have been added that takes a `RenderFragment` as input. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203).
1313
- Added a `ComponentParameterCollection` type. The `ComponentParameterCollection` is a collection of component parameters, that knows how to turn those components parameters into a `RenderFragment`, which will render a component and pass any parameters inside the collection to that component. That logic was spread out over multiple places in bUnit, and is now owned by the `ComponentParameterCollection` type. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203).
14+
- Added additional placeholder services for `NavigationManager`, `HttpClient`, and `IStringLocalizer`, to make it easier for users to figure out why a test is failing due to missing service registration before rendering a component. By [@joro550](https://github.com/joro550) in [#203](https://github.com/egil/bUnit/pull/223).
1415

1516
### Changed
1617
List of changes in existing functionality.
@@ -32,7 +33,8 @@ List of changes in existing functionality.
3233
)
3334
);
3435
```
35-
By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203).
36+
By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/203).
37+
- All test doubles are now in the same namespace, `Bunit.TestDoubles`. So all import statements for `Bunit.TestDoubles.JSInterop` and `Bunit.TestDoubles.Authorization` must be changed to `Bunit.TestDoubles`. By [@egil](https://github.com/egil) in [#203](https://github.com/egil/bUnit/pull/223).
3638

3739
### Deprecated
3840
List of soon-to-be removed features.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace Bunit.Docs.Samples
2+
{
3+
using Bunit;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using RichardSzalay.MockHttp;
6+
using System;
7+
using System.Net;
8+
using System.Net.Http;
9+
using System.Net.Http.Headers;
10+
using System.Text.Json;
11+
12+
public static class MockHttpClientBunitHelpers
13+
{
14+
public static MockHttpMessageHandler AddMockHttpClient(this TestServiceProvider services)
15+
{
16+
var mockHttpHandler = new MockHttpMessageHandler();
17+
var httpClient = mockHttpHandler.ToHttpClient();
18+
httpClient.BaseAddress = new Uri("http://localhost");
19+
services.AddSingleton<HttpClient>(httpClient);
20+
return mockHttpHandler;
21+
}
22+
23+
public static MockedRequest RespondJson<T>(this MockedRequest request, T content)
24+
{
25+
request.Respond(req =>
26+
{
27+
var response = new HttpResponseMessage(HttpStatusCode.OK);
28+
response.Content = new StringContent(JsonSerializer.Serialize(content));
29+
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
30+
return response;
31+
});
32+
return request;
33+
}
34+
35+
public static MockedRequest RespondJson<T>(this MockedRequest request, Func<T> contentProvider)
36+
{
37+
request.Respond(req =>
38+
{
39+
var response = new HttpResponseMessage(HttpStatusCode.OK);
40+
response.Content = new StringContent(JsonSerializer.Serialize(contentProvider()));
41+
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
42+
return response;
43+
});
44+
return request;
45+
}
46+
}
47+
}

docs/samples/tests/xunit/bunit.docs.xunit.samples.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8+
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
89
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
910
<PackageReference Include="xunit" Version="2.4.1" />
1011
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">

docs/site/docs/test-doubles/mocking-httpclient.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,32 @@ title: Mocking HttpClient
55

66
# Mocking `HttpClient`
77

8-
TODO - https://github.com/egil/bunit/issues/61
8+
Mocking the `HttpClient` service in .NET Core is a bit more clumbersome than interface-based services like `IJSRuntime`.
9+
There is currently no built-in mock for `HttpClient` in bUnit, but with the use of
10+
[RichardSzalay.MockHttp](https://www.nuget.org/packages/RichardSzalay.MockHttp/) we can easily add one that works
11+
with bUnit.
12+
13+
To use RichardSzalay.MockHttp, add the following package reference to your test project's .csproj file:
14+
15+
```xml
16+
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
17+
```
18+
19+
To make it easier to work with [RichardSzalay.MockHttp](https://www.nuget.org/packages/RichardSzalay.MockHttp/), add
20+
the following extension class to your test project. It makes it easier to add the `HttpClient` mock to
21+
bUnit's test context's `Services` collection, and configure responses to requests:
22+
23+
[!code-csharp[MockHttpClientBunitHelpers.cs](../../../samples/tests/xunit/MockHttpClientBunitHelpers.cs?start=3&end=46)]
24+
25+
With the helper methods in place, you can do the following in your tests:
26+
27+
```csharp
28+
using var ctx = new TestContext();
29+
var mock = ctx.Services.AddMockHttpClient();
30+
mock.When("/getData").RespondJson(new List<Data>{ ... });
31+
```
32+
33+
This registers the mock `HttpClient` in bUnit's test context's `Services` collection, and then tells the mock that when a request is received for `/getData`, it should respond with the `new List<Data>{ ... }`, serialized as JSON.
34+
35+
> [!TIP]
36+
> You can add addtional `RespondXXX` methods to the `MockHttpClientBunitHelpers` class to fit your testing needs.

docs/site/docs/toc.md

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

2424
# [Test Doubles](xref:test-doubles)
2525
## [Faking Authorization](xref:faking-auth)
26+
## [Mocking HttpClient](xref:mocking-httpclient)
2627
## [Mocking IJSRuntime](xref:mocking-ijsruntime)
2728

2829
# [Miscellaneous testing tips](xref:misc-test-tips)

src/bunit.template/template/CounterCSharpTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using Xunit;
33
using Bunit;
4-
using Bunit.TestDoubles.JSInterop;
4+
using Bunit.TestDoubles;
55
using static Bunit.ComponentParameterFactory;
66

77
namespace Company.BlazorTests1

src/bunit.template/template/_Imports.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
@using Microsoft.JSInterop
55
@using Microsoft.Extensions.DependencyInjection
66
@using Bunit
7-
@using Bunit.TestDoubles.JSInterop
7+
@using Bunit.TestDoubles
88
@using Xunit

src/bunit.web/Extensions/TestServiceProviderExtensions.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
using System.Net.Http;
12
using Bunit.Diffing;
23
using Bunit.Rendering;
3-
using Bunit.TestDoubles.Authorization;
4-
using Bunit.TestDoubles.JSInterop;
4+
using Bunit.TestDoubles;
55
using Microsoft.AspNetCore.Authorization;
6+
using Microsoft.AspNetCore.Components;
67
using Microsoft.AspNetCore.Components.Authorization;
78
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Localization;
810
using Microsoft.Extensions.Logging;
911
using Microsoft.Extensions.Logging.Abstractions;
1012
using Microsoft.JSInterop;
@@ -21,13 +23,20 @@ public static class TestServiceProviderExtensions
2123
/// </summary>
2224
public static IServiceCollection AddDefaultTestContextServices(this IServiceCollection services)
2325
{
26+
// Placeholders and defaults for common Blazor services
2427
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
2528
services.AddSingleton<AuthenticationStateProvider, PlaceholderAuthenticationStateProvider>();
2629
services.AddSingleton<IAuthorizationService, PlaceholderAuthorizationService>();
2730
services.AddSingleton<IJSRuntime, PlaceholderJSRuntime>();
31+
services.AddSingleton<NavigationManager, PlaceholderNavigationManager>();
32+
services.AddSingleton<HttpClient, PlaceholderHttpClient>();
33+
services.AddSingleton<IStringLocalizer, PlaceholderStringLocalization>();
34+
35+
// bUnit specific services
2836
services.AddSingleton<HtmlComparer>();
2937
services.AddSingleton<BunitHtmlParser>();
3038
services.AddSingleton<IRenderedComponentActivator, RenderedComponentActivator>();
39+
3140
return services;
3241
}
3342
}

src/bunit.web/TestDoubles/Authorization/AuthorizationState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Bunit.TestDoubles.Authorization
1+
namespace Bunit.TestDoubles
22
{
33
/// <summary>
44
/// Enumeration that represents the user's authorization state.

src/bunit.web/TestDoubles/Authorization/FakeAuthenticationStateProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using System.Threading.Tasks;
66
using Microsoft.AspNetCore.Components.Authorization;
77

8-
namespace Bunit.TestDoubles.Authorization
8+
namespace Bunit.TestDoubles
99
{
1010
/// <summary>
1111
/// Represents a fake implementation of AuthenticationStateProvider for testing purposes that allows

0 commit comments

Comments
 (0)