The article shows how to test an ASP.NET Core MVC API using xUnit and a HTTPClient client using Protobuf for the content formatters.
Code: https://github.com/damienbod/AspNetCoreMvcProtobufFormatters
Posts in this series:
- ASP.NET Core Using Protobuf Formatters
- Testing an ASP.NET Core MVC Protobuf API using HTTPClient and xUnit
2019-01-30 Updated to ASP.NET Core 2.2
2017-08-17 Updated ASP.NET Core 2.0
The test project tests the ASP.NET Core API produced here. xUnit is used as a test framework. The xUnit dependencies can be added to the test project using NuGet in Visual Studio 2017 as well as the Microsoft.AspNetCore.TestHost package. Microsoft provide nice docs about Integration testing ASP.NET Core.
When the NuGet packages have been added, you can view these in the csproj file, or install and update directly in this file. A reference to the project containg the API is also added to the test project.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <AssemblyName>AspNetCoreProtobuf.IntegrationTests</AssemblyName> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="xunit.runner.console" Version="2.4.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" /> <PackageReference Include="protobuf-net" Version="2.4.0" /> <PackageReference Include="xunit.runners" Version="2.0.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AspNetCoreProtobuf\AspNetCoreProtobuf.csproj" /> </ItemGroup> <ItemGroup> <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> </ItemGroup> </Project>
The TestServer is used to test the ASP.NET Core API. This is setup for all the API tests.
private readonly TestServer _server; private readonly HttpClient _client; public ProtobufApiTests() { _server = new TestServer( new WebHostBuilder() .UseKestrel() .UseStartup<Startup>()); _client = _server.CreateClient(); }
HTTP GET request test
The GetProtobufDataAndCheckProtobufContentTypeMediaType test sends a HTTP GET to the test server, and requests the content as application/x-protobuf. The result is deserialized using protobuf and the header and the expected result is checked.
[Fact] public async Task GetProtobufDataAndCheckProtobufContentTypeMediaType() { // Act _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf")); var response = await _client.GetAsync("/api/values/1"); response.EnsureSuccessStatusCode(); var result = ProtoBuf.Serializer.Deserialize<ProtobufModelDto>(await response.Content.ReadAsStreamAsync()); // Assert Assert.Equal("application/x-protobuf", response.Content.Headers.ContentType.MediaType ); Assert.Equal("My first MVC 6 Protobuf service", result.StringValue); }
HTTP POST request test
The PostProtobufData test method sends a HTTP POST request to the test server with a protobuf serialized content. The status code of the request is validated.
[Fact] public void PostProtobufData() { // HTTP GET with Protobuf Response Body _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf")); MemoryStream stream = new MemoryStream(); ProtoBuf.Serializer.Serialize<ProtobufModelDto>(stream, new ProtobufModelDto { Id = 2, Name= "lovely data", StringValue = "amazing this ah" }); HttpContent data = new StreamContent(stream); // HTTP POST with Protobuf Request Body var responseForPost = _client.PostAsync("api/Values", data).Result; Assert.True(responseForPost.IsSuccessStatusCode); }
The tests can be executed or debugged in Visual Studio using the Test Explorer
The tests can also be run with dotnet test in the commandline.
C:\git\damienbod\AspNetCoreProtobufFormatters\src\AspNetCoreProtobuf.IntegrationTests>dotnet test Build started, please wait... Build completed. Test run for C:\git\damienbod\AspNetCoreProtobufFormatters\src\AspNetCoreProtobuf.IntegrationTests\bin\Debug\netcoreapp1.1\AspNetCoreProtobuf.IntegrationTests.dll(.NETCoreApp,Version=v1.1) Microsoft (R) Testausführungs-Befehlszeilentool Version 15.0.0.0 Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. Die Testausf├╝hrung wird gestartet, bitte warten... [xUnit.net 00:00:00.5821132] Discovering: AspNetCoreProtobuf.IntegrationTests [xUnit.net 00:00:00.6841246] Discovered: AspNetCoreProtobuf.IntegrationTests [xUnit.net 00:00:00.7273897] Starting: AspNetCoreProtobuf.IntegrationTests info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http:// info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Executing action method AspNetCoreProtobuf.Controllers.ValuesController.Post (AspNetCoreProtobuf) with arguments (AspNetCoreProtobuf.Model.ProtobufModelDto) - ModelState is Valid info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreProtobuf.Controllers.ValuesController.Post (AspNetCoreProtobuf) in 137.2264ms info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 346.8796ms 200 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http:// info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Executing action method AspNetCoreProtobuf.Controllers.ValuesController.Get (AspNetCoreProtobuf) with arguments (1) - ModelState is Valid info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreProtobuf.Controllers.ValuesController.Get (AspNetCoreProtobuf) in 39.0599ms info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 43.2983ms 200 application/x-protobuf info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http:// info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Executing action method AspNetCoreProtobuf.Controllers.ValuesController.Get (AspNetCoreProtobuf) with arguments (1) - ModelState is Valid info: Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor[1] Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Executed action AspNetCoreProtobuf.Controllers.ValuesController.Get (AspNetCoreProtobuf) in 1.4974ms info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 3.6715ms 200 application/x-protobuf [xUnit.net 00:00:01.5669956] Finished: AspNetCoreProtobuf.IntegrationTests Tests gesamt: 3. Bestanden: 3. Fehler: 0. Übersprungen: 0. Der Testlauf war erfolgreich. Testausführungszeit: 2.7499 Sekunden
appveyor CI
The project can then be connected to any build server. Appveyor is a easy one to setup and works well with github projects. Create an account and select the github repository to build. Add an appveyor.yml file to the root of your project and configure as required. Docs can be found here:
https://www.appveyor.com/docs/build-configuration/
image: Visual Studio 2017 init: - git config --global core.autocrlf true install: - ECHO %APPVEYOR_BUILD_WORKER_IMAGE% - dotnet --version - dotnet restore build_script: - dotnet build before_build: - appveyor-retry dotnet restore -v Minimal test_script: - cd src/AspNetCoreProtobuf.IntegrationTests - dotnet test
The appveyor badges can then be used in your project md file.
| | Build | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | .NET Core | [](https://ci.appveyor.com/project/damienbod/aspnetmvc6protobufformatters) |
This would then be displayed in github as follows:
Links
https://developers.google.com/protocol-buffers/docs/csharptutorial
https://www.appveyor.com/docs/build-configuration/
https://www.nuget.org/packages/protobuf-net/
https://github.com/mgravell/protobuf-net
http://teelahti.fi/using-google-proto3-with-aspnet-mvc/
https://github.com/damienpontifex/ProtobufFormatter/tree/master/src/ProtobufFormatter
http://www.strathweb.com/2014/11/formatters-asp-net-mvc-6/
https://github.com/WebApiContrib/WebApiContrib.Formatting.ProtoBuf
https://damienbod.wordpress.com/2014/01/11/using-protobuf-net-media-formatter-with-web-api-2/
https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing
[…] Testing an ASP.NET Core MVC Protobuf API using HTTPClient and xUnit (Damien Bowden) […]
[…] for ASP.NET Core by Andrew Lock. What is the Microsoft.AspNetCore metapackage? by Andrew Lock. Testing an ASP.NET Core MVC Protobuf API using HTTPClient and xUnit by Damien Bowden. ASP.NET Core MVC Anatomy (Part 1) – AddMvcCore by Steve Gordon. Fritz’s […]