前言

上一篇提到了 NSubstitute,前輩看到後提點了一些用法,順勢研究完多一篇筆記紀錄一下(絕對不是我原本想要跳過不介紹被抓到才補上這篇)。

本文

驗證互動是否符合預期方法

接續上次使用範例。

情境:
我們現在有一個 Check 的方法,若出現錯誤則會紀錄一筆log。

首先先把 log 方法準備好。

    public interface ILog
    {
        void SaveLog(string message);
    }
    
    public class Log : ILog
    {
        public void SaveLog(string message)
        {
            Console.WriteLine(message);
        }
    }

Check 方法。

        bool Check(string text);
        
        public bool Check(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                this._log.SaveLog("text 不可為空!");
                return false;
            }
            return true;
        }

接下來我們先調整一下我們的測試方法。

        private ILog _log;
        private IRepository _repository;

        [TestInitialize]
        public void TestInitialize()
        { 
            this._log = Substitute.For<ILog>();
            this._repository = Substitute.For<IRepository>();
        }
        private Service GetSystemUnderTest()
        {
            var sut = new Service(this._repository,this._log);

            return sut;
        }

那我們測試方法就可以這麼做。

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("ServiceTest")]
        [TestProperty("ServiceTests", "Check")]
        public void log_是否有被呼叫()
        {
            //arrange
            var sut = GetSystemUnderTest();

            //act
            var actual = sut.Check(string.Empty);

            //assert
            actual.Should().BeFalse();
            this._log.Received(1);
        }

也可以驗證 log 記下的內容。

    [TestMethod()]
        [Owner("Sian")]
        [TestCategory("ServiceTest")]
        [TestProperty("ServiceTests", "Check")]
        public void log_有被呼叫_應記下正確內容()
        {
            //arrange
            var sut = GetSystemUnderTest();
            //act
            var actual = sut.Check(string.Empty);

            //assert
            actual.Should().BeFalse();
            this._log.Received(1).SaveLog(Arg.Is<string>(x => x.Contains("text")));
        }

若不應該收到訊息可以這麼測試。

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("ServiceTest")]
        [TestProperty("ServiceTests", "Check")]
        public void log_沒有被呼叫()
        {
            //arrange
            var sut = GetSystemUnderTest();
            //act
            var actual = sut.Check("123");

            //assert
            actual.Should().BeTrue();
            this._log.DidNotReceive();
        }

傳遞的參數

情境:
Insert 一筆資料到 DB 若成功回傳 True,失敗則 False。

Repository 準備

        /// <summary>
        /// 新增帳號
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        bool InsertAccount(AccountModel model);
        
        public bool InsertAccount(AccountModel model)
        {
            //你知道的這只是一種假設沒有要真的實作。
            return true;
        }

Service 的部分。

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        bool InserAccount(AccountModel model);
        
        public bool InserAccount(AccountModel model)
        {
            var result = this._repository.InsertAccount(model);

            return result;
        }

那我們的測試就可以這麼做。

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("ServiceTest")]
        [TestProperty("ServiceTests", "InsertAccount")]
        public void InsertAccount_成功_應回傳true()
        {
            //arrange
            var data = new AccountModel
            {
                Account = "123",
                UserName = "Sian"
            };
            var sut = GetSystemUnderTest();
            this._repository.InsertAccount(Arg.Any<AccountModel>()).Returns(true);

            //act
            var actual = sut.InserAccount(data);

            //assert
            actual.Should().BeTrue();
        }

可以看到傳遞給 Repository 的參數我們直接使用了 Args.Any 這個方式,並直接指定了回傳的內容,又被你學到一個偷懶的方式

同樣的驗證False也可以這麼做。

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("ServiceTest")]
        [TestProperty("ServiceTests", "InsertAccount")]
        public void InsertAccount_失敗_應回傳False()
        {
            //arrange
            var data = new AccountModel
            {
                Account = "123",
                UserName = "Sian"
            };
            var sut = GetSystemUnderTest();
            this._repository.InsertAccount(Arg.Any<AccountModel>()).Returns(false);

            //actr
            var actual = sut.InserAccount(data);

            //assert
            actual.Should().BeFalse();
        }

驗證執行的次數

情境:
有一個內有迴圈的方法。

    public interface ICommand
    {
        void Execute();
    }
    
    public class Command
    {
        ICommand _command;
        int _numberOfTimesToCall;
        public Command(
            ICommand command, 
            int numberOfTimesToCall)
        {
            this._command = command;
            this._numberOfTimesToCall = numberOfTimesToCall;
        }

        public void Execute()
        {
            for (var i = 0; i < this._numberOfTimesToCall; i++)
            {
                this._command.Execute();
            }
        }
    }

那我們測試就可以這麼做

         private ICommand _command;
        private int _numberOfTimesToCall;

        [TestInitialize]
        public void TestInitialize()
        {
            this._command = Substitute.For<ICommand>();
        }
        private Command GetSystemUnderTest()
        {
            var sut = new Command(this._command, this._numberOfTimesToCall);

            return sut;
        }

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("CommandTests")]
        [TestProperty("CommandTests", "Execute")]
        public void Execute_被執行了3次_正確計算應為3()
        {
            //arrange
            this._numberOfTimesToCall = 3;
            var sut = GetSystemUnderTest();

            //Act
            sut.Execute();

            //Assert
            this._command.Received(3).Execute();
        }

不為3則會報錯。

        [TestMethod()]
        [Owner("Sian")]
        [TestCategory("CommandTests")]
        [TestProperty("CommandTests", "Execute")]
        public void Execute_被執行了3次_正確計算應為3()
        {
            //arrange
            this._numberOfTimesToCall = 4;
            var sut = GetSystemUnderTest();

            //Act
            sut.Execute();

            //Assert
            this._command.Received(3).Execute();
        }

範例連結

這幾次示範範例連結會放在以下網址,歡迎取用。

參考連結