單元測試-測試替身

單元測試

在講Test Double以前,得先上搞清楚另外兩個

SUT:System Under Test/Software Under Test 【待測程式】,在Unit Test角度下待測程式會是method

DOC:Depended-on Component【相依元件】,例如:成立訂單函數,如果訂單失敗異常會寫入log,log函數就是成立訂單函數的DOC

在實務上筆者在寫單元測試的時候,因為SUT會去呼叫其他的物件也會呈現出SUT依賴DOC,
在測試SUT勢必會有DOC存在,搞得測試很複雜。
 

目前測試替身有五種Dummy Object、Test Stub、Test Spy、Fake Object與Mock Object
筆者目前是常用到Stub、Mock、Fake Object。
 

Dummy Objects用法:測試中必續傳入的物件,實際上這些物件根本不會用到,只是為了能夠調用背側物件而必須傳入的一個東西【目前沒用到】。

Stub用法:回傳固定的值。

    public class StubUserRepository : IUserRepository
    {
        public string GetUserRole(string username)
        {
            return "administrator";
        }

        public List<User> GetAllUsers()
        {
            return new List<User>()
            {
                new User{ Role = "administrator", Name = "admin"},
            };
        }
    }
    public interface IUserRepository
    {
        string GetUserRole(string username);
    }

    public class User
    {
        public string Name { get; set; }
        public string Role { get; set; }
    }

Fake用法:接近提供原始物件較簡單實作方式,Fake 就是寫一個簡易邏輯取代原本邏輯

    public class FakeUserRepository : IUserRepository
    {
        public string GetUserRole(string username)
        {
            if (username == "admin")
                return "administrator";
            else
                return "contributor";
        }
    }

    public interface IUserRepository
    {
        string GetUserRole(string username);
    }

Spy用法:把spy想成是一個偷偷紀錄呼叫過程的Stub,宣告被spy的物件,某個函式被呼叫的時候回傳某個值。
 

    public class SpyUserStore : IUserRepository
    {
        private static int Counter { get; set; }

        public SpyUserStore()
        {
            Counter = 0;
        }

        public string GetUserRole(string username)
        {

            if (Counter >= 1)
                throw new Exception("Function called more than once");

            Counter++;

            if (username == "admin")
                return "administrator";
            else
                return "contributor";
        }
    }

Mock用法:用動態程式方式、提供類似Dummy,Stub,Spy功能

   var _memberRepository = Substitute.For<IMemberRepository>();
   var _memberService = new MemberService(_memberRepository);
   var _setReturnMember = new Member()
        {
           UserId = "test001",
           Password = "test001",
           Name = "test",
           Email = "abc@gmail.com"
        };
   _memberRepository.GetByMember(userid,password).Returns(_setReturnMember);

最後總結一下,測試替身可以解決單元測試的那些問題?

  1. 測試替身是為了解決不用真的使用DOC
  2. 可以提升測試執行速度,避免速度太慢

另外其實在真的寫測試,也不用太在意你是用哪一個測試替身…

參考來源

  1. 搞笑談軟工-測試替身
  2. jyt0532’s Blog-測試替身

元哥的筆記