Posted on Leave a comment

單元測試 – 隔離框架Substitute.For

STUB的功能

這邊是NSubstitute的說明:http://nsubstitute.github.io/help/getting-started/
Substitute是.NET裡的一個隔離框架,若要使用,需要額外在測試專案用NUGET去安裝NSubstitute

1. 動態產生假物件
2. 模擬回傳值
3. 測試監聽事件
4. 驗證傳入參數是否正確

使用Subsitute(Sub)
使用方法如下
calculator = Substitute.For();
設定呼叫某個方法應該回傳的值
calculator.Add(1, 2).Returns(3);
Assert.That(calculator.Add(1, 2), Is.EqualTo(3));
下面可以驗證是否Add這個FUNC有被呼叫到
calculator.Add(1, 2);
calculator.Received().Add(1, 2);
calculator.DidNotReceive().Add(5, 7);
下面的程式能夠判別傳入的參數是不是正確
calculator.Add(10, -5);
calculator.Received().Add(10, Arg.Any());
calculator.Received().Add(10, Arg.Is(x => x < 0));[/code] 驗證回傳值是否正確 [code lang="C#"]calculator .Add(Arg.Any(), Arg.Any())
.Returns(x => (int)x[0] + (int)x[1]);
Assert.That(calculator.Add(5, 10), Is.EqualTo(15));

使用Substitute來針對不同狀況實作假介面

這是一個用MOCK方法的範例

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//using Microsoft.VisualStudio.TestTools.UnitTesting;
using NUnit.Framework;
using RsaSecureToken;
using Assert = NUnit.Framework.Assert;

namespace RsaSecureToken.Tests
{
    [TestFixture]
    public class AuthenticationServiceTests
    {
        [Test()]
        public void IsValidTest()
        {
            var target = new AuthenticationService(new FakeProfile(), new FakeToken());

            var actual = target.IsValid("joey", "91000000");

            //always failed
            Assert.IsTrue(actual);
        }
    }

    public class FakeProfile:IProfile
    {

        public string GetPassword(string account)
        {
            if (account == "joey")
            {
                return "91";
            }

            throw new Exception();
        }
    }

    public class FakeToken:IRsaToken
    {
        public string GetRandom(string account)
        {
            return "000000";
        }
    }
}

上面做法有什麼問題?

  • 每一個不同的依賴案例就要製作一個不同的FakeObject,會讓寫測試的時間太久
  • 沒辦法直接從程式碼知道為什麼這樣會是Vaild

動態產生物件的使用完整範例

Subsitute.For()
定義假物件行為(stub)
fake.方法(參數).Returns(值)
[Test()]
public void IsValidTest()
{
var fakeProfile = Substitute.For();
fakeProfile.GetPassword(“joey”).Returns(“91”);

var fakeToken = Substitute.For();
fakeToken.GetRandom(“”).ReturnsForAnyArgs(“000000”);

var target = new AuthenticationService(fakeProfile, fakeToken);
var actual = target.IsValid(“joey”, “91000000”);

//always failed
Assert.IsTrue(actual);
}
}

驗證某個函數是否被呼叫

使用mock object assertion
需求:驗證是非法的時候要記一個log
fake.Receive(次數).方法(參數驗證)
不要用太多mock就算要使用要避免過度指定,也就是當prod code小小變動就導致測試程式壞掉

下面的程式碼可以驗證當呼叫SyncBookOrders()時是不是會呼叫Insert這個函數兩次:
[Test]
public void Test_SyncBookOrders_3_Orders_Only_2_book_order()
{
var result = new List
{
new Order
{
Type = “Book”
},
new Order
{
Type = “Book”
},
new Order
{
Type = “Item”
}
};

var target = new OrderServiceForTest();
target.SetOrder(result);

var fakeBookDao = Substitute.For();
target.SetDao(fakeBookDao);

target.SyncBookOrders();

fakeBookDao.Received(2).Insert(Arg.Is(m => m.Type == “Book”));
}

驗證傳入參數

驗證傳入的參數是否包含某些關鍵字
_logger.Received(1).Save(Arg.Is(m => m.Contains(“joey”) && m.Contains(“login failed”)));

Leave a Reply