Skip to content

Commit 9e46d9e

Browse files
Add baseline unit coverage and Verify-based public API approvals; wire CI to solution tests (#211)
* test: add unit and API approval tests Co-authored-by: mauroservienti <1325611+mauroservienti@users.noreply.github.com> * chore: remove committed test result artifact Co-authored-by: mauroservienti <1325611+mauroservienti@users.noreply.github.com> * test: rename factory test file for clarity Co-authored-by: mauroservienti <1325611+mauroservienti@users.noreply.github.com> * test: use PublicApiGenerator with Verify for API approval Co-authored-by: mauroservienti <1325611+mauroservienti@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mauroservienti <1325611+mauroservienti@users.noreply.github.com>
1 parent 24fdd94 commit 9e46d9e

File tree

6 files changed

+157
-4
lines changed

6 files changed

+157
-4
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ jobs:
3535
8.0.x
3636
9.0.x
3737
10.0.x
38-
- name: Build
39-
run: dotnet build src --configuration Release
40-
- name: Tests
41-
run: dotnet test src --configuration Release --no-build
38+
- name: Build
39+
run: dotnet build src/ServiceComposer.AspNetCore.Testing.sln --configuration Release
40+
- name: Tests
41+
run: dotnet test src/ServiceComposer.AspNetCore.Testing.sln --configuration Release --no-build
4242
- name: Upload packages
4343
if: matrix.name == 'Linux'
4444
uses: actions/upload-artifact@v6.0.0
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/ServiceComposer/ServiceComposer.AspNetCore.Testing")]
2+
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")]
3+
namespace ServiceComposer.AspNetCore.Testing
4+
{
5+
public class SelfContainedWebApplicationFactoryWithHost<TEntryPoint> : Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>
6+
where TEntryPoint : class
7+
{
8+
public SelfContainedWebApplicationFactoryWithHost(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection> configureServices, System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder> configure, string[] args = null) { }
9+
public System.Action<Microsoft.Extensions.Hosting.IHostBuilder> HostBuilderCustomization { get; set; }
10+
public System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder> WebHostBuilderCustomization { get; set; }
11+
protected override Microsoft.Extensions.Hosting.IHostBuilder CreateHostBuilder() { }
12+
}
13+
public class SelfContainedWebApplicationFactoryWithWebHost<TEntryPoint> : Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>
14+
where TEntryPoint : class
15+
{
16+
public SelfContainedWebApplicationFactoryWithWebHost(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection> configureServices, System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder> configure) { }
17+
public SelfContainedWebApplicationFactoryWithWebHost(System.Action<Microsoft.AspNetCore.Hosting.WebHostBuilderContext, Microsoft.Extensions.DependencyInjection.IServiceCollection> configureServices, System.Action<Microsoft.AspNetCore.Hosting.WebHostBuilderContext, Microsoft.AspNetCore.Builder.IApplicationBuilder> configure) { }
18+
public System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder> BuilderCustomization { get; set; }
19+
protected override void ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { }
20+
protected override Microsoft.AspNetCore.Hosting.IWebHostBuilder CreateWebHostBuilder() { }
21+
}
22+
public class WebApplicationFactoryWithWebHost<TStartup> : Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TStartup>
23+
where TStartup : class
24+
{
25+
public WebApplicationFactoryWithWebHost() { }
26+
public System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder> BuilderCustomization { get; set; }
27+
protected override void ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { }
28+
protected override Microsoft.AspNetCore.Hosting.IWebHostBuilder CreateWebHostBuilder() { }
29+
}
30+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using PublicApiGenerator;
2+
using VerifyXunit;
3+
4+
namespace ServiceComposer.AspNetCore.Testing.Tests;
5+
6+
public class PublicApiApprovalTests
7+
{
8+
[Fact]
9+
public Task Public_api_is_approved()
10+
{
11+
var publicApi = ApiGenerator.GeneratePublicApi(typeof(WebApplicationFactoryWithWebHost<>).Assembly);
12+
return Verifier.Verify(publicApi);
13+
}
14+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Hosting;
5+
6+
namespace ServiceComposer.AspNetCore.Testing.Tests;
7+
8+
public class SelfContainedWebApplicationFactoryTests
9+
{
10+
[Fact]
11+
public async Task SelfContainedFactoryWithHost_serves_request()
12+
{
13+
await using var factory = new SelfContainedWebApplicationFactoryWithHost<TestEntryPoint>(
14+
services => services.AddSingleton(new Message("host")),
15+
app => app.Run(async context =>
16+
{
17+
var message = context.RequestServices.GetRequiredService<Message>();
18+
await context.Response.WriteAsync(message.Value);
19+
}));
20+
21+
var client = factory.CreateClient();
22+
var response = await client.GetStringAsync("/");
23+
24+
Assert.Equal("host", response);
25+
}
26+
27+
[Fact]
28+
public async Task SelfContainedFactoryWithWebHost_invokes_builder_customization()
29+
{
30+
var builderCustomizationCalled = false;
31+
await using var factory = new SelfContainedWebApplicationFactoryWithWebHost<TestEntryPoint>(
32+
services => services.AddSingleton(new Message("webhost")),
33+
app => app.Run(async context =>
34+
{
35+
var message = context.RequestServices.GetRequiredService<Message>();
36+
await context.Response.WriteAsync(message.Value);
37+
}))
38+
{
39+
BuilderCustomization = _ => builderCustomizationCalled = true
40+
};
41+
42+
var client = factory.CreateClient();
43+
var response = await client.GetStringAsync("/");
44+
45+
Assert.Equal("webhost", response);
46+
Assert.True(builderCustomizationCalled);
47+
}
48+
49+
[Fact]
50+
public async Task WebApplicationFactoryWithWebHost_invokes_builder_customization()
51+
{
52+
var builderCustomizationCalled = false;
53+
await using var factory = new WebApplicationFactoryWithWebHost<Startup>
54+
{
55+
BuilderCustomization = _ => builderCustomizationCalled = true
56+
};
57+
58+
var client = factory.CreateClient();
59+
var response = await client.GetStringAsync("/");
60+
61+
Assert.Equal("startup", response);
62+
Assert.True(builderCustomizationCalled);
63+
}
64+
65+
class TestEntryPoint;
66+
67+
class Startup
68+
{
69+
public void Configure(IApplicationBuilder app)
70+
{
71+
app.Run(context => context.Response.WriteAsync("startup"));
72+
}
73+
}
74+
75+
record Message(string Value);
76+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="coverlet.collector" Version="6.0.4" />
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
13+
<PackageReference Include="PublicApiGenerator" Version="11.5.4" />
14+
<PackageReference Include="Verify.Xunit" Version="31.12.5" />
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<Using Include="Xunit" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<ProjectReference Include="..\ServiceComposer.AspNetCore.Testing\ServiceComposer.AspNetCore.Testing.csproj" />
25+
</ItemGroup>
26+
27+
</Project>

src/ServiceComposer.AspNetCore.Testing.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ VisualStudioVersion = 15.0.26730.16
44
MinimumVisualStudioVersion = 10.0.40219.1
55
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceComposer.AspNetCore.Testing", "ServiceComposer.AspNetCore.Testing\ServiceComposer.AspNetCore.Testing.csproj", "{37843543-9921-4A2E-A62E-EC64E08A8BA6}"
66
EndProject
7+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceComposer.AspNetCore.Testing.Tests", "ServiceComposer.AspNetCore.Testing.Tests\ServiceComposer.AspNetCore.Testing.Tests.csproj", "{83B393CD-C479-4306-B2B5-56EB3B75170E}"
8+
EndProject
79
Global
810
GlobalSection(SolutionConfigurationPlatforms) = preSolution
911
Debug|Any CPU = Debug|Any CPU
@@ -14,6 +16,10 @@ Global
1416
{37843543-9921-4A2E-A62E-EC64E08A8BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
1517
{37843543-9921-4A2E-A62E-EC64E08A8BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
1618
{37843543-9921-4A2E-A62E-EC64E08A8BA6}.Release|Any CPU.Build.0 = Release|Any CPU
19+
{83B393CD-C479-4306-B2B5-56EB3B75170E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20+
{83B393CD-C479-4306-B2B5-56EB3B75170E}.Debug|Any CPU.Build.0 = Debug|Any CPU
21+
{83B393CD-C479-4306-B2B5-56EB3B75170E}.Release|Any CPU.ActiveCfg = Release|Any CPU
22+
{83B393CD-C479-4306-B2B5-56EB3B75170E}.Release|Any CPU.Build.0 = Release|Any CPU
1723
EndGlobalSection
1824
GlobalSection(SolutionProperties) = preSolution
1925
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)