Moqを使ってUnitTestする

tera1707.com

やりたいこと

UnitTestをするときに便利だと評判のMoqを使って、UTしたい。

参考になるサイト、サンプルコードを挙げているサイトはやまほどあったが、自分の練習のためと部品取りのために自分のサンプルを置いておきたい。

サンプルコード

取り合えず、対象Mockクラスの中のプロパティの値、メソッドの値を自由に制御できるようにしておけば、かなり便利に&自分で書くコード少なくUTができるとおもうので、そのあたりのサンプルを挙げておく。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MyClass;
using System;

namespace TestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            ///////////////////////
            // mockプロパティの練習
            ///////////////////////
            var mock = new Mock<IMyClass>();

            ///////////////////////
            // mockプロパティをsetするときのお作法
            mock.SetupProperty(x => x.Bairitsu, 15);//これをやらないと、下でプロパティに代入できない
            mock.Object.Bairitsu = 10;
            Assert.AreEqual(10, mock.Object.Bairitsu);

            ///////////////////////
            // mockプロパティをgetするときのお作法
            mock.SetupGet(x => x.Bairitsu)
                .Returns(33);
            Assert.AreEqual(33, mock.Object.Bairitsu);
        }

        [TestMethod]
        public void TestMethod2()
        {
            ///////////////////////
            // mockメソッドの練習
            ///////////////////////
            var mock = new Mock<IMyClass>();

            ///////////////////////
            // メソッドの戻り値を指定する

            // 引数がこの値だったらこの値を返す
            mock.Setup(x => x.Add(1, 2))
                .Returns(100);

            Assert.AreEqual(100, mock.Object.Add(1, 2));
            Assert.AreEqual(0, mock.Object.Add(1, 3));

            ///////////////////////
            // 呼ばれるごとに返す値を変化させる
            mock.SetupSequence(x => x.Add(1, 2))
                .Returns(100) // 1回目よばれたとき
                .Returns(101) // 2回目よばれたとき
                .Returns(102);// 3回目よばれたとき

            Assert.AreEqual(100, mock.Object.Add(1, 2));
            Assert.AreEqual(101, mock.Object.Add(1, 2));
            Assert.AreEqual(102, mock.Object.Add(1, 2));

            ///////////////////////
            // 第一引数 < 2、第二引数 < 4、だったら20を返す(それ以外は0)
            mock.Setup(x => x.Add(It.Is<int>(x => x < 2), It.Is<int>(x => x < 4)))
                .Returns(20);

            Assert.AreEqual(20, mock.Object.Add(1, 3));
            Assert.AreEqual(0, mock.Object.Add(1, 5));

            ///////////////////////
            // 第一引数 = 10、第二引数 = 20、だったら例外をスロー
            mock.Setup(x => x.Add(10, 20))
                .Throws<InvalidOperationException>();

            mock.Object.Add(1, 3); // 例外おきない
            Assert.ThrowsException<InvalidOperationException>(() =>
            {
                mock.Object.Add(10, 20);//InvalidOperationException発生
            });

            ///////////////////////
            // モックが指定の動作で呼ばれたかどうか確認
            mock.Object.Add(1, 2);//100

            mock.Verify(x => x.Add(1, 2), 
                        Times.AtLeastOnce());    //1回呼ばれてるのでMockException発生しない
            Assert.ThrowsException<MockException>(() =>
            {
                mock.Verify(x => x.Add(99, 100),
                            Times.AtLeastOnce()); //1回も呼ばれてないのでMockException発生する 
            });
        }
    }
}

// テスト対象IF
namespace MyClass
{
    public interface IMyClass
    {
        int Bairitsu { get; set; }
        int Add(int a, int b);
    }

    public class MyClass1 : IMyClass
    {
        public int Bairitsu { get; set; }
        public int Add(int a, int b) => a + b;
    }
}

参考

Moqリポジトリ

github.com

dk521123.hatenablog.com

qiita.com