4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# visual studioでMSTestを使ったユニットテスト

Last updated at Posted at 2024-05-08

ソフトウェアテストにおけるユニットテストについて :eyes:

ユニットテストとは

ユニットテストの特徴

  • 関数やメソッド等の個々のユニットを対象としたテスト
  • 自動化され、コードを変更した際に繰り返し実行される
  • 変更による不具合を検知(ふるまいの違い、デグレ防止)
  • 保守時のバグによるリスクが減少する
  • 単体テストとも言う

ユニットテストは、開発者が行う最小単位に対するテスト

visual stuidoのテストツール

Enterprise Edition 限定のテストツール

  • Live Unit Testing : ユニットテストをリアルタイムで定期的に実行
  • IntelliTest : ユニットテストを自動生成
  • コードカバレッジ : ユニットテストの割合を調べる

テストフレームワーク

代表的なテストフレームワークは、以下の3つ

  • MSTest : visual studio に統合されたMicrosoft製のテストフレームワーク
  • xUnit.net : OSS、クロスプラットフォームのテストフレームワーク
  • NUnit : xUnitと同様にOSS、クロスプラットフォーム対応。JUnitに近い

MSTestは、visual studioに組み込まれているので導入の敷居が低い。また、V2では他と同様にOSSとなり、クロスプラットフォームでの開発が可能となった。

Star の比較

GitHubのStarを比較すると、xUnitが一番多い :star:

xUnit.net 4k > NUnit 2.5k > MSTest 651

※ 2024.5.7現在

テストプロジェクト作成

visual studioでは、テストコードを別プロジェクトとして作成する
MSTestを例にテストプロジェクトの作成方法を確認する

.NET Framework の場合

ユニットテストを実装したいメソッドを右クリックして、[単体テストの作成]をクリック
image.png

単体テストの作成ウィンドウが開くので、OKをクリック
image.png

MSTestのテストプロジェクトが作成される
image.png

作成されたプロジェクトには、先ほど右クリックしたメソッドのテストコードが自動生成される

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConsoleDotNetFramework.UnitTest.Tests
{
    [TestClass]
    public class CalcTests
    {
        [TestMethod]
        public void AddTest()
        {
            Assert.Fail();
        }
    }
}

.NET Core の場合

ソリューションを右クリックして、[追加]→[新しいプロジェクト]をクリック
image.png

MSTestテストプロジェクトを選択して次へ
image.png

作成したテストプロジェクトの依存関係を右クリックして、[プロジェクト参照の追加]
image.png

テスト対象のプロジェクトにチェックを入れる
image.png

これでテストプロジェクトの準備が完了 :sunny:

ユニットテストの実行

ユニットテストのクラスには、[TestClass]属性(アノテーション)を付与。
テストメソッドには、[TestMethod]属性を付与することで、テストフレームワークにユニットテストであることを認識させる
()は無くても動作した

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConsoleDotNetFramework.UnitTest.Tests
{
    [TestClass()]
    public class CalcTests
    {
        [TestMethod()]
        public void Add_Number_ReturnsTotalNumber()
        {
            // Arrange
            var calc = new Calc();

            // Act
            var actual = calc.Add(2, 3);

            // Assert
            Assert.AreEqual(5, actual);
        }
    }
}

テストコードが実装できたら、ツールバーの[テスト]→[すべてのテストを実行]を選択するか、ctr + Rの後にAでテストを実行する
image.png

テストエクスプローラーが展開し、ユニットテストの実行結果が表示される
image.png

ユニットテスト対象のメソッド側にもテストの実行結果が表示される
image.png

Assertクラス

Assertクラスは複数用意されており、テストケースに応じて使い分ける

Assert Class

Assert Classは、値型、参照型、Null判定、真偽値の判定などが可能
visual studioからF12で定義に飛び、メソッドを確認するのが一番わかりやすいと思う

CollectionAssert Class

CollectionAssert Classは、オブジェクトのコレクションを比較する場合や、コレクションの状態を確認する場合に使用する
要素同士の比較や、要素が含まれているか、Nullが含まれているか、要素がすべて一意であるか等

StringAssert Class

StringAssert Classは、文字列のテストに特化したAssertクラス

テストメソッド例

テストメソッドのサンプルコードを示す

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace ConsoleDotNetFramework.UnitTest.Tests
{
    [TestClass()]
    public class CalcTests
    {
        /// <summary>
        /// 値の比較
        /// </summary>
        [TestMethod()]
        public void Add_Number_ReturnsTotalNumber()
        {
            var calc = new Calc();

            var actual = calc.Add(2, 3);

            Assert.AreEqual(5, actual);
        }

        /// <summary>
        /// 複数の値を比較
        /// </summary>
        /// <param name="expected"></param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        [DataTestMethod]
        [DataRow(3, 1, 2)]
        [DataRow(5, -1, 6)]
        public void Add_ParamNumbers_ReturnsTotalNumber(
            int expected, int a, int b)
        {
            var calc = new Calc();

            var actual = calc.Add(a, b);

            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        /// 参照元が同じかどうかを比較
        /// </summary>
        [TestMethod]
        public void Compare_SameClass()
        {
            var calc = new Calc();
            
            var calc2 = calc;
            
            Assert.AreSame(calc, calc2);
        }

        /// <summary>
        /// 参照元が異なるかどうかを比較
        /// </summary>
        [TestMethod]
        public void Compare_NotSameClass()
        {
            var calc = new Calc();

            var calc2 = new Calc();            

            Assert.AreNotSame(calc, calc2);
        }

        /// <summary>
        /// コレクションの要素を比較
        /// </summary>
        [TestMethod]
        public void Compare_ListValues()
        {
            var list = new List<int>() { 1, 2 };

            var list2 = new List<int>() { 1, 2 };

            CollectionAssert.AreEqual(list, list2);
        }

        /// <summary>
        /// コレクションの要素を比較(インデックスが異なる場合)
        /// </summary>
        [TestMethod]
        public void Compare_ListValues_differentIndex()
        {
            var list = new List<int>() { 1, 2 };

            var list2 = new List<int>() { 2, 1 };

            CollectionAssert.AreNotEqual(list, list2);
        }

        /// <summary>
        /// コレクションの要素を比較(インデックスが異なる場合)
        /// </summary>
        [TestMethod]
        public void Compare_ListValues_Equivalent()
        {
            var list = new List<int>() { 1, 2 };

            var list2 = new List<int>() { 2, 1 };

            // AreEquivalentは、インデックスが異なる場合でもテスト成功とする
            CollectionAssert.AreEquivalent(list, list2);
        }

        /// <summary>
        /// コレクションの要素を比較(一意かどうか)
        /// </summary>
        [TestMethod]
        public void Compare_ListValues_IsUnique()
        {
            var list = new List<int>() { 1, 2 };
            
            CollectionAssert.AllItemsAreUnique(list);
        }

        /// <summary>
        /// コレクションの要素を比較(含まれているか)
        /// </summary>
        [TestMethod]
        public void Compare_ListValues_Contains()
        {
            var list = new List<int>() { 1, 2 };

            CollectionAssert.Contains(list, 1);
        }

        /// <summary>
        /// 例外が発生するかどうかをテスト
        /// </summary>
        [TestMethod]
        public void Throw_InvalidOperationException()
        {
            var calc = new Calc();

            int? num = null;

            // 例外の型を厳密に判断する
            Assert.ThrowsException<InvalidOperationException>(() => calc.Add((int)num, 1));
        }

        /// <summary>
        /// 文字列が含まれているかをテスト
        /// </summary>
        [TestMethod]
        public void Contains_value()
        {
            var value = "abcd";
            var subString = "abc";

            StringAssert.Contains(value, subString);
        }

        /// <summary>
        /// 指定した文字列で始まるかをテスト(プレフィックスの確認)
        /// </summary>
        [TestMethod]
        public void StartWith_value()
        {
            var value = "abcd";
            var subString = "abc";

            StringAssert.StartsWith(value, subString);
        }

        /// <summary>
        /// 指定した文字列で終わるかをテスト(サフィックスの確認)
        /// </summary>
        [TestMethod]
        public void EndsWith_value()
        {
            var value = "abcd";
            var subString = "cd";

            StringAssert.EndsWith(value, subString);
        }
    }
}

Reference

4
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?