iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0

看程式碼說故事-3

在昨天 Day-10 把 EmailSystem 從 JJEmail 這隻套件抽離;同時,在測試方面也針對 EmailFunction 撰寫了一隻帶有虛設常式的測試,可以看到在最後測試的時候寫了一隻百分百會回傳 "Success" 的功能。

然而,這樣的測試實際上是沒有意義的;其原因在於

  1. 實際商業邏輯上並不會去呼叫 StubEmailSerivce,而是用呼叫第三方套件的 DemoJJEmailSerivce。
  2. StubEmailSerivce 的 SendEmail 百分百是回傳 "Success",如果驗證失敗也只是代表在撰寫假物件的過程中有誤,增加開發成本。
using NUnit3;

[TestFixture]
public class EmailSystemUnitTests
{
    [Test]
    public void EmailFunction_Success()
    {
        // Arrange
        StubEmailSerivce stubEmailService = new StubEmailSerivce();
        
        EmailSystem EmailService = new EmailSystem(stubEmailService);
        
        // Act
        var stubResult = EmailSystem.EmailFunction("Test@abc.com.tw", "Test Demo");
        
        // Assert
        Assert.AreEqual("Success", stubResult);
    }
}

public class StubEmailSerivce : IEmailService
{
    public string SendEmail(EmailAddress, EmailMessage)
    {
        return "Success";
    }
}

那既然如此,為何還需要虛設常式呢?在這之前,我們先換種角度觀察,來看看商業邏輯的流程,也就是 EmailFunction。

在昨天 Day-10 中,我們有提到 EmailSystem 的 EmailFunction 目前的功能:

  1. 利用 EmailServiece 的 SendEmail 方法取得寄信結果 EmailFinalResult (string 格式)。
  2. 回傳 EmailFinalResult。
public class EmailSystem
{
    ...

    public string EmailFunction(string EmailAddress, string EmailMessage)
    {
        var EmailFinalResult = EmailServiece.SendEmail(EmailAddress, EmailMessage);

        return EmailFinalResult;
    }
}

那假設今天變更需求,要在寄送 Email 之前確認 EmailMessage 是否帶有髒字(是否定義為髒字牽涉到語義學,為了不模糊主題,我們僅偵測是否帶有“傻瓜”字眼),若不符合條件則回傳失敗。因此,程式碼改寫如下:

public class EmailSystem
{
    ...

    public string EmailFunction(string EmailAddress, string EmailMessage)
    {
        // 確認是否帶有髒字
        var SendEmail = false;
        
        if (!EmailMessage.Contains("傻瓜"))
        {
            SendEmail = true;
        }
        
        // 寄送 E-mail 方法
        var EmailFinalResult = "Fail";
        
        if (SendEmail == true)
        {
            var EmailFinalResult = EmailServiece.SendEmail(EmailAddress, EmailMessage);
        } 

        return EmailFinalResult;
    }
}

寫到這邊,是不是測試的項目也要隨之變動了,而不再是單純測試是否會回傳 “Success” 這個功能。而是要針對是否帶有“傻瓜”字眼判別寄信成果;因此,單元測試程式碼如下:

using NUnit3;

[TestFixture]
public class EmailSystemUnitTests
{
    [Test]
    public void EmailFunction_NotContainFool_Success()
    {
        // Arrange
        StubEmailSerivce stubEmailService = new StubEmailSerivce();
        
        EmailSystem EmailService = new EmailSystem(stubEmailService);
        
        // Act
        var Result = EmailSystem.EmailFunction("Test@abc.com.tw", "Test Demo");
        
        // Assert
        Assert.AreEqual("Success", Result);
    }
    
    [Test]
    public void EmailFunction_ContainFool_Fail()
    {
        // Arrange
        StubEmailSerivce stubEmailService = new StubEmailSerivce();
        
        EmailSystem EmailService = new EmailSystem(stubEmailService);
        
        // Act
        var Result = EmailSystem.EmailFunction("Test@abc.com.tw", "傻瓜 呵呵");
        
        // Assert
        Assert.AreEqual("Fail", Result);
    }
}

public class StubEmailSerivce : IEmailService
{
    public string SendEmail(EmailAddress, EmailMessage)
    {
        return "Success";
    }
}

所以,在這邊測試的目的就很明確了,就是要測試是否有正確判別 EmailMessage 是否能針對字串帶有 “傻瓜” 的字眼做出正確的判別。而 StubEmailSerivce 所做的事情就是要把 EmailServiece.SendEmail(EmailAddress, EmailMessage) 的影響排除掉。

以作者自己的觀點,微軟大多很熱門的套件大多不會有出錯的可能性,但今天是使用合作公司或來路不明的 .dll 檔案(舉例來說,在政府接專案可能是承接前一家廠商做到一半的東西,而交接東西不完全的情況)。此時,這個 .dll 黑盒子的可信度就有待評估。因此,透過虛設常式就可以過濾這種風險。

明天會開始介紹虛設常式的好兄弟——模擬物件 (Mock)。


上一篇
Day 10-假物件 (Fake) - 虛設常式 (Stub)-2 (核心技術-2)
下一篇
Day 12-假物件 (Fake) - 模擬物件 (Mock)-1 (核心技術-4)
系列文
單元測試從入門到進階之路 (以 C# NUnit 3 X NSubstitute 為例)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言