Taurus を使って .NET のテストプロジェクトを対象にロードテストを実施する


はじめに

下記の記事で紹介されていた、Taurus のドキュメントを見ていたら NUnit で作成したテストケースも負荷テストのシナリオに使えそうということで試してみました。

Windows へのインストールなどは下記のドキュメントを参照してください。

テスト対象とテストプロジェクトの準備

テスト対象のプロジェクトと、テストプロジェクトを作成してソリューションに追加します。
テスト対象のプロジェクトは .NET 6.0 でも問題ありませんが、テストプロジェクトは、今のところ .NET Core 3.1 を対象にしないと動かないので注意してください。

mkdir LoadTestSample
cd LoadTestSample
dotnet new web -o LoadTestSample.WebApp
dotnet new nunit -o LoadTestSample.WebApp.Tests -f netcoreapp3.1
dotnet new sln
dotnet sln add LoadTestSample.WebApp/LoadTestSample.WebApp.csproj
dotnet sln add LoadTestSample.WebApp.Tests/LoadTestSample.WebApp.Tests.csproj

作成された Web アプリを実行します。

> cd LoadTestSample/LoadTestSample.WebApp
> dotnet run
ビルドしています...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7043
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5049
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\localrepo\LoadTestSample\LoadTestSample.WebApp\

アプリは https://localhost:7043 で実行されているようなので、テストではこのエンドポイントをリクエストして、200 が返ってくるかを確認します。

Test1.cs
using NUnit.Framework;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;

namespace LoadTestSample.WebApp.Tests
{
    public class Test1
    {
        private HttpClient _httpClient;
        [SetUp]
        public void Setup()
        {
            _httpClient = new HttpClient();
        }

        [Test]
        public async Task Test1()
        {
            var response = await _httpClient.GetAsync("https://localhost:7043");
            Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
        }
    }
}

とりあえずテストがうまく動くか実行してみます。

> cd LoadTestSample/LoadTestSample.WebApp
> dotnet test
Microsoft (R) Test Execution Command Line Tool Version 17.1.0
Copyright (c) Microsoft Corporation.  All rights reserved.

テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。

成功!   -失敗:     0、合格:     1、スキップ:     0、合計:     1、期間:  - LoadTestSample.WebApp.Tests.dll (netcoreapp3.1)           

問題なさそうですね。

テスト定義ファイルの追加と実行

公式のサンプルをもとに、テスト定義ファイルを作成します。
とりあえず 50 同時で、シナリオを 100 回実行するようにします。

loadtest.yml
execution:
- executor: nunit
  concurrency: 50
  iterations: 100
  scenario:
    script: LoadTestSample.WebApp.Tests\bin\Debug\netcoreapp3.1\LoadTestSample.WebApp.Tests.dll

上記の yml を引数に bzt を起動します。

> cd LoadTestSample
> bzt .\loadtest.yml
11:28:08 INFO: Taurus CLI Tool v1.16.3
11:28:08 INFO: Starting with configs: ['C:\\Users\\杉山洋一\\.bzt-rc', '.\\loadtest.yml']
11:28:08 INFO: Configuring...
11:28:08 INFO: Artifacts dir: C:\localrepo\LoadTestSample\2022-03-22_11-28-08.675839
11:28:08 INFO: Preparing...
11:28:08 INFO: Starting...
11:28:08 INFO: Waiting for results...
11:28:09 INFO: Did not mute console logging
11:28:09 INFO: Waiting for finish...
11:29:45 INFO: Changed data analysis delay to 187s
11:30:07 WARNING: Please wait for graceful shutdown...
11:30:07 INFO: Shutting down...
11:30:08 INFO: Post-processing...
11:30:09 INFO: Test duration: 0:02:00
11:30:09 INFO: Samples count: 5000, 0.00% failures
11:30:09 INFO: Average times: total 1.223, latency 0.000, connect 0.000
11:30:09 INFO: Percentiles:
+---------------+---------------+
| Percentile, % | Resp. Time, s |
+---------------+---------------+
|           0.0 |         0.004 |
|          50.0 |         0.196 |
|          90.0 |         0.336 |
|          95.0 |         0.402 |
|          99.0 |        49.248 |
|          99.9 |        93.632 |
|         100.0 |        93.632 |
+---------------+---------------+
11:30:09 INFO: Request label stats:
+-------+--------+---------+--------+-------+
| label | status |    succ | avg_rt | error |
+-------+--------+---------+--------+-------+
| Test1 |   OK   | 100.00% |  1.223 |       |
+-------+--------+---------+--------+-------+
11:30:09 INFO: Artifacts dir: C:\localrepo\LoadTestSample\2022-03-22_11-28-08.675839
11:30:09 INFO: Done performing with code: 0

一応、無事テストは正常終了したようです。
テスト中はこんな感じのウインドウが表示されました。

Counter の確認

.NET 内部の状況を確認するには、dotnet counters サブコマンドを利用するとわかりやすいです。インストールしていない場合は、次のコマンドラインからインストールできます。

dotnet tool install -g dotnet-counters

dotnet counters ps で現在コンピューターで実行されている .NET のプロセスを表示し、dotnet counters collect で収集を開始します。Q をタイプすると収集が完了し、CSV ファイルに結果が出力されます。

> dotnet counters ps
     20940 dotnet     C:\Program Files\dotnet\dotnet.exe
      3828 LoadTestSample.WebApp C:\localrepo\LoadTestSample\LoadTestSample.WebApp\bin\Debug\net6.0\LoadTestSample.WebApp.exe
     13740 pwsh       C:\Program Files\PowerShell\7\pwsh.exe
     
> dotnet counters collect --process-id 3828   
--counters is unspecified. Monitoring System.Runtime counters by default.
Starting a counter session. Press Q to quit.
File saved to counter.csv

Excel のピボットグラフにピボットしてみると、Gen0 のメモリが大量に作成されているのがわかりますね。まぁ、今回のテスト対象は Hello World を表示しているだけなのであんまり面白い結果にはなりませんでしたね。

おわりに

詳しくはまだ触れていないのですが、API の E2E テストを NUnit などで書いている場合は、Taurus を使えばそのまま負荷テストにも転用できそうですね。