iT邦幫忙

2021 iThome 鐵人賽

DAY 10
1

看程式碼說故事 (Stub-2)

前景提要,昨天提到開發者要開發 Email 通知系統,於是他就利用 JJEmail 套件裡面的 SendEmail 方法解決 Email 通知系統裡面的寄信功能,並回傳寄信的情況(資料型別為一串字串);但在 Code Review 階段被質疑說測試出現問題時,無法判別結果失敗是因為程式碼撰寫有誤,還是因為套件出錯,此外,測試找不到適當的切入點撰寫假物件。

因此,今天要先解決商業邏輯程式碼透過依賴反轉的概念,設計介面並讓 JJEmail 用依賴注入的方式寫進邏輯,以下為目前的流程圖:

https://ithelp.ithome.com.tw/upload/images/20210912/20127378lfRw1jjKS2.png


在單元測試的藝術中曾提到一個讓測試介入更簡單的觀點:任何物件導向的問題,都可以透過增加一個中介層(a layer of indirection)來解決;當然,除了中介層過多的這個問題;換言之,從 C# 的角度看這段話,一個簡單又好用的做法就是在 EmailFunction 與 JJEmail 之間設立介面,程式碼如下:

public class EmailSystem
{
    private IEmailService EmailService;

    public EmailSystem(IEmailService inEmailService)
    {
        EmailService = inEmailService;
    }

    public string EmailFunction(string mailAddress, string mailMessage)
    {
        var SendResult = EmailService
                        .SendEmail(mailAddress, mailMessage);

        return SendResult;
    }
}

public interface IEmailService()
{
    public string SendEmail(mailAddress, mailMessage);
}

利用建構函式的概念,已經將 EmailSystem 當中的 EmailFunction 這隻功能從 JJEmail 抽離,而若要實作該介面(如利用 JJEmail),程式碼範例如下:

using JJEmail;

public class DemoJJEmailSerivce : IEmailService
{
    var EmailService = new JJEmailService();
    
    public string SendEmail(mailAddress, mailMessage)
    {
        var EmailFinalResult = EmailServiece
                              .SendEmail(mailAddress, mailMessage);
        
        return EmailFinalResult;
    }
}

而此時的流程圖已改成如下:

https://ithelp.ithome.com.tw/upload/images/20210912/201273785bG8xJevnV.png


OK,介面抽離的部分終於處理完了,可以到這次的重點:虛設常式 (Stub),以這個例子,虛設常式要做的事情就是把 IEmailService 的功能實作出來並執行 EmailFunction 是否有誤。所以,測試碼的撰寫如下:

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";
    }
}

好,看到這邊,會注意到 StubEmailService 把 DemoJJEmailSerivce 的實作給替換掉,換成一隻不管參數是什麼,都一定會回傳 "Success" 的功能;但是,如果到這邊就結束,其實有點在做白工,因為特定寫了一個百分之百會回傳 "Success" 的方法,然後再呼叫這隻方法驗證是否其正確,在實務上是一件很奇怪的事情。

所以接下來明天會再針對這部分在做進一步探討,探討如果 EmailFunction 邏輯再擴充,對於測試上的影響,以及 Stub 真正的用意是什麼。


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

尚未有邦友留言

立即登入留言