MStestによるクラスライブラリのデバッグ

# 概要
テストフレームワークを利用したクラスライブラリのテスト方法を紹介します。
デバッグ用のダミーMain関数を使用しているよりもテストフレームワークを使用した方がメリットがあります。

- 判定結果を集計してくれる
- 例外がスローされるといった同作も確認できる
- テストパターンを一気に実行もできるし、個別実行もできる

## 環境
- IDE：VisualStudio2022
- 言語：C#
- テストフレームワーク：MStest

xUnitなどのテストフレームワークの方がメジャーかもしれませんが、**デフォで並列実行**されるテストフレームワークは**ハードウェアが絡む検証には多少厄介**です。
つまりデバイスの多重OPENなどの問題を懸念しています。


# お題
TCPで受け取ったメッセージをModbusのヘッダに変換するクラスをのテストをお題とします。
平たく言うと、**Byte型の配列を読み取って構造体に入れ込む**だけです。

ModBusに関する説明は割愛しますので、下の画からやりたいことを察してください。

![Modbusデータ構造.dio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/58430694-b064-0f6a-3540-8d5da22806c4.png)



# 手順
## ①クラスライブラリの新規作成

まずVisualStudioで新規クラス作成します

ソリューション名：MySolution
クラス名(プロジェクト名)：ModBus
フレームワーク：.NET6

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/73c9d90d-581f-8aef-e716-d4854c6f7ec2.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/78402e10-e109-0195-cec1-0f6d030d4cb1.png)

### テスト対象であるクラスライブラリのコード
```CS:Modbus.cs
namespace modbus{
    public class ModBus{
        private const int   HEADER_SIZE    = 7;//
        /// <summary>
        /// ModBusヘッダ構造体の定義
        /// </summary>
        public struct typModBusHeader{
            public ushort   TransactionID;  //トランザクションＩＤ
            public ushort   ProtocolID;     //プロトコルＩＤ
            public ushort   DataLength;     //データ長
            public byte     UnitID;         //ユニットＩＤ
        }

        public  typModBusHeader     Header; //ヘッダ構造体をクラス変数として持つ

        ///<summary>
        ///コンストラクタ(あり得なそうな値で初期化)
        ///</summary>
        public ModBus(){
            this.Header.TransactionID  = 0xFFFF;
            this.Header.ProtocolID     = 0xFFFF;
            this.Header.DataLength     = 0xFFFF;
            this.Header.UnitID         = 0xFF;
        }
        /// <summary>
        /// バイト配列をセットする
        /// </summary>
        public void SetByteData(byte[] byteData){
            //配列のデータサイズ不足していたら例外を投げる
            if (byteData.Length < HEADER_SIZE)
                throw new ArgumentException("input data size Shortage!");
            //Ｂｙｔｅデータをヘッダ構造体に詰める
            this.Header.TransactionID  = (ushort)((byteData[0] <<8) | byteData[1]);
            this.Header.ProtocolID     = (ushort)((byteData[2] <<8) | byteData[3]);
            this.Header.DataLength     = (ushort)((byteData[4] <<8) | byteData[5]);
            this.Header.UnitID         = (byte)(byteData[6]);
        }
    }
}
```

## ②テストフレームワークの追加
新しいプロジェクト作成にてMSTestを選択します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/b49ef406-d2cd-eeaf-ac0e-2a03752604b9.png)

テストプロジェクトをテスト対象のクラスライブラリのあるソリューションに追加してください。
（ソリューションを新規作成しないでください）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/6d03cba8-3103-ed39-2c8e-d5c8fca53ef5.png)

依存関係、参照の設定からテスト対象のクラスモジュールにチェックを入れてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/5a1e69f7-732e-2231-54cf-c5e03dc56ae5.png)

もしくは、csprjファイルを書き換えていただく方法でも構いません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/14bdb3db-21b2-5890-3bf2-ed6ec2ab469e.png)


### テストコード
```CS:UnitTest1.cs
using modbus;
namespace test_space{
    [TestClass]
    public class UnitTest{ 
        ModBus              mb = new ModBus();//テスト対象のクラスをインスタンス
        ArgumentException   ex = new ArgumentException();//例外メッセージを受け取るためのオブジェクト

        /// <summary>
        /// 入力データサイズ不足していた場合例外がスローされるテスト
        /// </summary>
        [TestMethod]
        public void Test001(){
            //テスト入力
            byte[] test_in = {0x11,0x22,0x33,0x44,0x55,0x66};
            //例外が発生するかチェック
            ex = Assert.ThrowsException<ArgumentException>(() => mb.SetByteData(test_in));
            //例外メッセージが期待通りであるかチェック
            Assert.AreEqual("input data size Shortage!", ex.Message);
        }

         /// <summary>
         /// Byte配列が期待した並び順でヘッダに格納されていることを確認するテスト
         /// </summary>
        [TestMethod]
        public void Test002(){
            //テスト入力
            byte[] test_in = {0x11,0x22,0x33,0x44,0x55,0x66,0x77};
            //try{
            mb.SetByteData(test_in);
            //Byte配列が期待した並び順でヘッダに格納されていることを確認
            Assert.AreEqual((ushort)0x1122, mb.Header.TransactionID);
            Assert.AreEqual((ushort)0x3344, mb.Header.ProtocolID);
            Assert.AreEqual((ushort)0x5566, mb.Header.DataLength);
            Assert.AreEqual((byte)  0x77  , mb.Header.UnitID);
        }
    }
}
```

# 実行方法
1. スタートアッププロジェクトをテストプロジェクトにする
1. ビルドする
1. テストエクスエクスプローラーを表示
1. 実行する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/bf630123-ebb4-5dbb-0435-b084fc67e151.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/bb0d6970-71d7-c4b9-1957-f23012dbce6c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/bf63a576-9094-cc01-3701-c35b4831ceff.png)

こんな感じでテストを実行可能です。