.net core持続集積試験編の試験方法改造
7715 ワード
シリーズディレクトリ
前の2つのセクションで説明したように、私たちのテストクラスにはすでに2つのテスト方法があります.全体的には次のようになります.
以上の方法は問題がないように見えますが、実際には性能の罠があります.私たちは前の章の知識を通じて、xunitのテストクラスの構造関数はすべてのテスト方法が実行されている間に1回実行されます.通常、私たちのテストコードは3つ以上ではありません.数十個から数百個もあります.これにより、毎回1つ作成するのは性能に非常に影響する.そしてここのTestServerと_クライアントは解放されなかったまた、Webプロジェクトでは、テストクラスごとにこのようなTestServerを作成する必要がある場合があります.このように重複するコードは何度もコピーされ、メンテナンスが困難になります.
前述のように、1つのオブジェクトを1つのテストクラスで1回だけ初期化するには、このクラスにIClassFixture汎用インタフェースを実装し、クラスは初期化時にこの汎用オブジェクトのエンティティに自動的に注入され、1回だけ初期化され、この汎用オブジェクトがIDisposableインタフェースを実装と、テストクラスのすべてのメソッドが実行完了したときにこのオブジェクトのDisposeメソッドが実行される.
まず、
ここでの方法とパラメータの大部分は、前にテストクラスに追加したものと同じですが、以下の点に注意してください.サーバ変数をコンストラクション関数の外に置くと、Disposeで解放されます.そうしないと位置決めできません.2.clientをpublicタイプに変更します.テストクラスにアクセスする必要があるからです.
次に、テストクラスの改造後のコードを見てみましょう.
ここではメインコードです.まずIClassFixtureを実装します.次に、無パラメトリック構造関数をパラメトリックに変更し、MyTestServer Fixtrueタイプのオブジェクトに転送します.Xunitは自動的にこのオブジェクトに注入されます.その後、このオブジェクトのhttpclientをこのクラスに割り当てます.クライアントオブジェクトは、このクラスで使用できます.
このように他のテストクラスでも
前述の2つの試験方法の提出経路には「/HelloWorld」が含まれており、実際にはコントローラ名に一致しており、一般的に同じコントローラでの方法の試験方法は同じ試験クラスに書かれている.このようにController名は固定されており,Actionの後のルーティングのみを必要とする単独で抽出することができる.
テストクラスを次のように変更しました.
ここではcontrollerの名前をHttpClientのBaseUrlに追加し、get、postなどのリクエストを送信する際にActionの名前だけを使用します.ここではnameofキーワードを使用してactionの名前を取得します.nameofキーワードを使用して取得するメリットは、第一に、メソッド名をクリックすることで指定した方法に迅速に位置付けることができます.さらに重要なのは、メソッドの名前が変更されると、コンパイル時にコンパイルエラーが発生し、エラーにすばやくナビゲートして修正することができます.
上のMyTestServerFixtrueクラスのコードには明らかな問題があります.それは、UseContentRootのパスが死んでいることです.プロジェクトのローカルアドレスがサーバ上または他の同僚とはほとんど異なる場合です(プロジェクトのディレクトリ名が異なるため)他の人がこれらのコードを呼び出すとエラーが発生する可能性があります.
この問題を解決するために、相対経路を用いて絶対路を取得することができる.この2つのプロジェクトのメインフォルダは同じフォルダの下にあるため、テストプロジェクトはいくつかの層を外に退けてmvcプロジェクトのメインディレクトリを得ることができる.
今回私たちはrootPathを書くのではなく、方法
まず、現在のプログラムドメインのディレクトリ、すなわちプログラムの実行ディレクトリを取得し、それを取得した後、mvcプロジェクトとこのtestプロジェクトを含むフォルダに何層上に移動できるかを見てみましょう.私たちはそれらを組み合わせてPathを通ります.GetFullPathは相対的な経路の絶経路を取得する.
サーバの障害により、要求が非常に遅くなり、サーバが長い間要求を返すことができなくなり、統合テストコードが「カード」で完了しない場合があります.この場合、タイムアウトを設定することができます.設定は非常に簡単で、HttpClientにはTimeout属性があり、対応するタイムアウト時間を設定すればよい.HttpClientのデフォルト要求タイムアウト時間は100 sであり、この値はほとんど変更する必要はないはずであるが、具体的な業務については、実行時間が特に長い(業務ロジックが非常に複雑で、sql文が非常に複雑であるなど)場合、ユニットで今回の要求にタイムアウト時間を設定することができる.例えば150 sですが、以下のように設定されています.
ここで、CancellationTokenSourceオブジェクトを定義し、タイムアウト時間を指定し、そのオブジェクトのTokenオブジェクトを非同期要求メソッドに渡す.
前の2つのセクションで説明したように、私たちのテストクラスにはすでに2つのテスト方法があります.全体的には次のようになります.
public class mvc20
{
private readonly HttpClient _client;
public mvc20()
{
var builder = new WebHostBuilder()
.UseContentRoot(@"E:\personal project
ewTest2018\ConsoleApp1\CoreMvc")
.UseEnvironment("Development")
.UseStartup();
var server = new TestServer(builder);
_client = server.CreateClient();
}
[Fact]
public async Task SimpleGet()
{
var response = await _client.GetAsync("/HelloWorld/Hello");
response.EnsureSuccessStatusCode();
var responseStr = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello,World", responseStr);
}
[Theory]
[AutoData]
public async Task SimplePost(Student stud)
{
var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
var response = await _client.PostAsync("/HelloWorld/StudentInfo", content);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
Assert.True(!string.IsNullOrEmpty(result));
}
}
改善1:オブジェクトの初期化を外部クラスに移動
以上の方法は問題がないように見えますが、実際には性能の罠があります.私たちは前の章の知識を通じて、xunitのテストクラスの構造関数はすべてのテスト方法が実行されている間に1回実行されます.通常、私たちのテストコードは3つ以上ではありません.数十個から数百個もあります.これにより、毎回1つ作成するのは性能に非常に影響する.そしてここのTestServerと_クライアントは解放されなかったまた、Webプロジェクトでは、テストクラスごとにこのようなTestServerを作成する必要がある場合があります.このように重複するコードは何度もコピーされ、メンテナンスが困難になります.
前述のように、1つのオブジェクトを1つのテストクラスで1回だけ初期化するには、このクラスにIClassFixture汎用インタフェースを実装し、クラスは初期化時にこの汎用オブジェクトのエンティティに自動的に注入され、1回だけ初期化され、この汎用オブジェクトがIDisposableインタフェースを実装と、テストクラスのすべてのメソッドが実行完了したときにこのオブジェクトのDisposeメソッドが実行される.
まず、
MyTestServerFixtrue
というクラスを作成し、TestServerとHttpClientオブジェクトの初期化をここで行います.コードは次のとおりです.public class MyTestServerFixtrue:IDisposable
{
public readonly HttpClient _client;
private readonly TestServer _server;
public MyTestServerFixtrue()
{
var builder = new WebHostBuilder()
.UseContentRoot(@"E:\personal project
ewTest2018\ConsoleApp1\CoreMvc")
.UseEnvironment("Development")
.UseStartup();
_server = new TestServer(builder);
_client = _server.CreateClient();
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
ここでの方法とパラメータの大部分は、前にテストクラスに追加したものと同じですが、以下の点に注意してください.サーバ変数をコンストラクション関数の外に置くと、Disposeで解放されます.そうしないと位置決めできません.2.clientをpublicタイプに変更します.テストクラスにアクセスする必要があるからです.
次に、テストクラスの改造後のコードを見てみましょう.
public class mvc20:IClassFixture
{
private readonly HttpClient _client;
public mvc20(MyTestServerFixtrue fixtrue)
{
this._client = fixtrue._client;
}
}
ここではメインコードです.まずIClassFixtureを実装します.次に、無パラメトリック構造関数をパラメトリックに変更し、MyTestServer Fixtrueタイプのオブジェクトに転送します.Xunitは自動的にこのオブジェクトに注入されます.その後、このオブジェクトのhttpclientをこのクラスに割り当てます.クライアントオブジェクトは、このクラスで使用できます.
このように他のテストクラスでも
IClassFixture
を実現することができ、TestServerの構成を変更するにはMyTestServer Fixtrueクラスで変更するだけでよい.改善2:固定ルーティングパラメータ
前述の2つの試験方法の提出経路には「/HelloWorld」が含まれており、実際にはコントローラ名に一致しており、一般的に同じコントローラでの方法の試験方法は同じ試験クラスに書かれている.このようにController名は固定されており,Actionの後のルーティングのみを必要とする単独で抽出することができる.
テストクラスを次のように変更しました.
public class mvc20:IClassFixture
{
private readonly HttpClient _client;
public mvc20(MyTestServerFixtrue fixtrue)
{
var baseAddr = fixtrue._client.BaseAddress.AbsoluteUri;
string controllerName ="HelloWorld";
this._client = fixtrue._client;
if (!fixtrue._client.BaseAddress.AbsoluteUri.Contains(controllerName))
{
fixtrue._client.BaseAddress = new Uri(baseAddr + controllerName+"/");
}
}
[Fact]
public async Task SimpleGet()
{
var response = await _client.GetAsync($"{nameof(HelloWorldController.Hello)}");
response.EnsureSuccessStatusCode();
var responseStr = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello,World", responseStr);
}
[Theory]
[AutoData]
public async Task SimplePost(Student stud)
{
var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
var response = await _client.PostAsync($"{nameof(HelloWorldController.StudentInfo)}", content);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
Assert.True(!string.IsNullOrEmpty(result));
}
}
ここではcontrollerの名前をHttpClientのBaseUrlに追加し、get、postなどのリクエストを送信する際にActionの名前だけを使用します.ここではnameofキーワードを使用してactionの名前を取得します.nameofキーワードを使用して取得するメリットは、第一に、メソッド名をクリックすることで指定した方法に迅速に位置付けることができます.さらに重要なのは、メソッドの名前が変更されると、コンパイル時にコンパイルエラーが発生し、エラーにすばやくナビゲートして修正することができます.
改善3:リソースパスを相対パスに変更
上のMyTestServerFixtrueクラスのコードには明らかな問題があります.それは、UseContentRootのパスが死んでいることです.プロジェクトのローカルアドレスがサーバ上または他の同僚とはほとんど異なる場合です(プロジェクトのディレクトリ名が異なるため)他の人がこれらのコードを呼び出すとエラーが発生する可能性があります.
この問題を解決するために、相対経路を用いて絶対路を取得することができる.この2つのプロジェクトのメインフォルダは同じフォルダの下にあるため、テストプロジェクトはいくつかの層を外に退けてmvcプロジェクトのメインディレクトリを得ることができる.
MyTestServerFixtrue
クラスの構造方法を以下のように変更しました.public MyTestServerFixtrue()
{
var rootPath = GetContentRootDir();
var builder = new WebHostBuilder()
.UseContentRoot(rootPath)
.UseEnvironment("Development")
.UseStartup();
_server = new TestServer(builder);
_client = _server.CreateClient();
}
今回私たちはrootPathを書くのではなく、方法
GetContentRootDir
で取得しました.このGetContentRootDir
の方法を見てみましょう private string GetContentRootDir()
{
var currentPath = AppDomain.CurrentDomain.BaseDirectory;
var relativePath = @"..\..\..\..\CoreMvc";
var combinedPath = Path.Combine(currentPath, relativePath);
var absPath = Path.GetFullPath(combinedPath);
return absPath;
}
まず、現在のプログラムドメインのディレクトリ、すなわちプログラムの実行ディレクトリを取得し、それを取得した後、mvcプロジェクトとこのtestプロジェクトを含むフォルダに何層上に移動できるかを見てみましょう.私たちはそれらを組み合わせてPathを通ります.GetFullPathは相対的な経路の絶経路を取得する.
改善四設定タイムアウト
サーバの障害により、要求が非常に遅くなり、サーバが長い間要求を返すことができなくなり、統合テストコードが「カード」で完了しない場合があります.この場合、タイムアウトを設定することができます.設定は非常に簡単で、HttpClientにはTimeout属性があり、対応するタイムアウト時間を設定すればよい.HttpClientのデフォルト要求タイムアウト時間は100 sであり、この値はほとんど変更する必要はないはずであるが、具体的な業務については、実行時間が特に長い(業務ロジックが非常に複雑で、sql文が非常に複雑であるなど)場合、ユニットで今回の要求にタイムアウト時間を設定することができる.例えば150 sですが、以下のように設定されています.
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(150));
var response = await client.GetAsync("/Home/index", cts.Token);
ここで、CancellationTokenSourceオブジェクトを定義し、タイムアウト時間を指定し、そのオブジェクトのTokenオブジェクトを非同期要求メソッドに渡す.