なんかゲームしたり、物買ったり、プログラムしてたりしてます
ASP.NET Coreを細々と勉強しています。ASP.NET Coreでテストはどうやればいいんだろうと思い調べてみました。
特に、URLベースでアクセスしてそのレスポンスに対して精査をするという事をしようと思います。
今回は公式ドキュメントとか若干記述が足りないところがあり、スタックオーバーフローなどをさまよう羽目になりました。
同じような事で困っている人の役に立てたらうれしいかなと思っています(´・ω・`)
Contents
今回はついにリリースされたVisual Studio for Macで作業しましたが、別にWindowsでも変わらないと思います。
各プロジェクトは単純に作成してビルドだけしています。
テストプロジェクト側には、ASP.NET Coreのプロジェクト(TestApp01)を参照しておきます。
また、パッケージとして「Microsoft.AspNetCore.TestHost」を加えます。
当初Integration testing | Microsoft Docsを見ながら作業を行ったのですが、どうしてもうまく行かず四苦八苦しました。
このドキュメントのまま作業をすると
といった現象に悩まされました。
これらを参照したところ無事テストが実行出来るようになりました。
色々ごちゃごちゃ細かく書くよりも作ったものを書きたいと思います。
using Xunit;
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;
using System.Reflection;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.CodeAnalysis;
using TestApp01;
namespace TestApp01Tests
{
public static class WebHostBuilderExtensions
{
private static string ContentPath
{
get
{
var path = PlatformServices.Default.Application.ApplicationBasePath;
var contentPath = Path.GetFullPath(Path.Combine(path, $@"../../../../{nameof(TestApp01)}"));
return contentPath;
}
}
public static IWebHostBuilder ConfigureTestContent(this IWebHostBuilder builder)
{
return builder.UseContentRoot(ContentPath);
}
public static IWebHostBuilder ConfigureTestServices(this IWebHostBuilder builder)
{
return builder.ConfigureServices(services =>
{
services.AddMvcCore();
services.Configure((RazorViewEngineOptions options) =>
{
var previous = options.CompilationCallback;
options.CompilationCallback = (context) =>
{
previous?.Invoke(context);
var assembly = typeof(Startup).GetTypeInfo().Assembly;
var assemblies = assembly.GetReferencedAssemblies()
.Select(x => MetadataReference.CreateFromFile(Assembly.Load(x).Location))
.ToList();
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("mscorlib")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Private.Corelib")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Html.Abstractions")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Razor")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Razor.Runtime")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Mvc")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Threading.Tasks")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Dynamic.Runtime")).Location));
assemblies.Add(MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Text.Encodings.Web")).Location));
context.Compilation = context.Compilation.AddReferences(assemblies);
};
});
});
}
}
public class UnitTest1
{
private readonly TestServer _server;
private readonly HttpClient _client;
public UnitTest1()
{
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestContent()
.ConfigureTestServices()
);
_client = _server.CreateClient();
}
[Fact]
public async Task Test1()
{
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
// Assert
Assert.Contains("Hello World!",
responseString);
}
}
}
大半がテスト用のホストの準備です。テスト本体はUnitTest1クラスのTest1メソッドとなります。
UnitTest1クラスのコンストラクタでテスト用のホストとクライアント作成し、それを利用してテストを行います。
コンストラクタでConfigureTestContentやConfigureTestServicesが呼ばれています。
これらが上の方で定義しているWebHostBuilderExtensionsクラスで拡張メソッドとして定義されています。
テストプロジェクトでそのままホストを起動すると、コンテンツのルートディレクトリの位置が変わってしまいます。
そのために、appsetting.jsonファイルなどが読めずにエラーとなります。
この拡張メソッドではそのコンテンツルートディレクトの正しい位置を設定するという機能を持っています。
上のConfigureTestContent拡張メソッドでコンテンツのディレクトリを正しく設定したとしてもViewの描画でエラーが発生します。
View上で使用している各種クラスがみつからないようです。
.NET初心者でいまいちよくわかっていないのですが、このメソッドにより各クラスのアセンブリを読み込ませる事によりエラーが発生しなくなるということだと思います。
無事テスト出来るようになったんですが、View関連はもう少しスマートになりませんでしょうかね。。。個人的にテストフレームワーク使ってテストってほとんどやったことないので、何もかもが勉強でした(;・∀・)