ここ数年 C# からは離れていたのですが久しぶりに触ることになったのでテスト周りがどうなっているのか調べている途中で TestHost に出会いました。
Microsoft.AspNetCore.TestHost を使った Integration Test に関して
ASP.NET Core でのテストの統合 | Microsoft Docs
自動翻訳で微妙に読みにくいですが、上記が一つのポインタになると思います。
RSpec だと request spec 辺りになるかと思います。feature spec のようなブラウザにレンダリングさせての End-to-End テストとは異なりますね。
Web API を作っているケースで使えそうという感じです。
最も簡単なテスト
これは上に貼ったリンクのサンプルです。
以下の Startup
はテスト対象の ASP.NET Core アプリケーション側のクラスです。WebHost としてビルドしたものを TestServer
に渡しているだけですね。
後は、そこから作成できるクライアントでテストを書いていけます。
public class PrimeWebDefaultRequestShould
{
private readonly TestServer _server;
private readonly HttpClient _client;
public PrimeWebDefaultRequestShould()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task ReturnHelloWorld()
{
// Act
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal("Hello World!",
responseString);
}
}
MVC の場合
上記のドキュメントの中盤の「Mvc Razor/テストの統合」にもあるように以下の設定をテスト側の csproj に追加しなくてはいけません。
<PropertyGroup>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
ただ、これだけだと動作しません。
- ASP.NET CoreでIntegration Test | トコトコ日記
- asp.net mvc - Razor compilation fails on TestHost when rendering view xUnit tests - Stack Overflow
- Creating instance of TestServer throws exception after upgrading to VS 2017 · Issue #959 · aspnet/Hosting
- Integration tests with ASP.NET Core causes missing references from Razor files · Issue #1212 · aspnet/Razor
- How do I solve a "view not found" exception in asp.net core mvc project - Stack Overflow
上記のようにいろいろと workaround があるようですが、たいてい以下のことをやっています。
-
shadowCopy: false
を設定したxunit.runner.json
を配置 - deps.json をコピーするビルドタスクを追加
<Target Name="CopyAditionalFiles" AfterTargets="Build" Condition="'$(TargetFramework)'!=''">
<ItemGroup>
<DepsFilePaths Include="$([System.IO.Path]::ChangeExtension('%(_ResolvedProjectReferencePaths.FullPath)', '.deps.json'))" />
</ItemGroup>
<Copy SourceFiles="%(DepsFilePaths.FullPath)" DestinationFolder="$(OutputPath)" Condition="Exists('%(DepsFilePaths.FullPath)')" />
</Target>
- テスト対象のプロジェクトを ContentRoot とするように
UseContentRoot
を指定
var asm = typeof(Startup).Assembly.GetName().Name;
string appRootPath = Path.GetFullPath(Path.Combine(
AppContext.BaseDirectory,
"..", "..", "..", "..", asm));
_server = new TestServer(new WebHostBuilder()
.UseContentRoot(appRootPath) // 💡 これを追加
.UseStartup<Startup>());
2.1 でその辺りがよくなる
Improved end to end testing support for MVC applications · Issue #275 · aspnet/Announcements
ここでケアされるのは以下のようです。
- It copies the .deps file from your project into the test assembly bin folder.
- It sets the content root the application's project root so that static files and views can be found.
- It provides a class WebApplicationTestFixture that streamlines the bootstrapping of your app on TestServer.
上記には無い xunit でのシャドウコピーに関しては引き続き行う必要があると書いてありますね。
内容については以下らへんで実際に見ることができます。(すでに preview も出ているので試すこともできるのかな)
WebApplicationTestFixture
の実装や、deps.json
のコピーに関する build target ファイルもありますね。
上記に加えて MVC 側から MvcTestFixture
が提供されるようです。
サンプル
以下に今回検証したサンプルを置いておきます。
(ホントは E2E テストじゃないんですが、最初は E2E テストが書きたくて試し始めたので名が体を表してないのは許してください)
E2eTestSample
は Visual Studio for Mac で MVC プロジェクトを作ってそのままです。
E2eTest
は以下のようになっています。
- TestUsingWebHostBuilderDirectly.cs
- workaround の実装の通りに
UseContentRoot
を使ったもの
- workaround の実装の通りに
- TestUsingInfrastracture.cs
-
infrastructure/
にコピーしてきた、2.1 で追加されるものを利用して書き直したもの
-
infrastructure
ディレクトリのファイルは、dev
ブランチから今回のお試しに必要なものを取ってきただけです。WebHostBuilderExtensions
は以下の aspnet/Hosting
の一部です。
その他 ASP.NET Core 2.0 での Integration Test の参考になりそうなもの
- API Series Part 6 - ASP.NET Core 2.0 and Integration Testing — Jack Vanlightly
- Testing an ASP.NET Core application using TestServer - Meziantou's blog
- donbavand/dotnetcore-integration-tests-example: Create a simple test integration framework in .NET Core 2.0
- Integration Testing in .NET Core 2.0
- Introduction to integration testing with xUnit and TestServer in ASP.NET Core