天天看點

【C#】【xUnit】【Moq】.NET單元測試Mock架構Moq初探!

在TDD開發模型中,經常是在編碼的同時進行單元測試的編寫,由于現代軟體開發不可能是一個人完成的工作,是以在定義好接口的時候我們就可以進行自己功能的開發(接口不能經常變更),而我們調用他人的功能時隻需要使用接口即可。

但我們在編寫自己的單元測試并進行功能驗證的時候,如果接口的實作人還沒有完成代碼怎麼辦呢?一般我們可能會自己寫一個模拟實作來進行單元測試,這就是我們經常所說的單元測試中的Stub和Mock(關于單元測試的Stub和Mock,可以自己度娘一下,也可以參考

https://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html

, 本文的部分代碼來自于這篇部落格)。在.net環境中可以使用的Mock架構是Moq,目前版本4.10。

我們使用NuGet安裝依賴的庫xUnit,Moq等。

我們定義兩個接口:

public interface IWebService
    {
        void LogError(string msg);
    }

    public interface IEmailService
    {
        void SendEmail(string a, string b, string c, string d);
    }           

一個類:

public class LogAnalyzer
    {
        private IWebService service;
        private IEmailService email;

        public IWebService Service
        {
            get { return service; }
            set { service = value; }
        }

        public IEmailService Email
        {
            get { return email; }
            set { email = value; }
        }

        public void Analyze(string fileName)
        {
            if (fileName.Length < 8)
            {
                try
                {
                    service.LogError("the file name is to short" + fileName);
                }
                catch (Exception e)
                {
                    email.SendEmail("[email protected]", "[email protected]", "IWebServiceFailed", e.Message);
                }
            }
        }
    }           

我們要進行這個類的測試,其中兩個接口的實作是别人來做。我在自己的單元測試中不想去引用他人的實作,也不想自己寫Mock,是以使用架構Moq來建立我想要的對象。

public class LogAnalyzerTest
    {
        [Fact(DisplayName = "使用MOQ架構")]
        public void AnalyzeTest()
        {
            var mockWebService = new Mock<IWebService>();
            mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws(new Exception());
            var mockEmailService = new Mock<IEmailService>();
            var a = mockEmailService.Setup(e => e.SendEmail("[email protected]", "[email protected]", "IWebServiceFailed", It.Is<string>(x=>x != null)));
            LogAnalyzer log = new LogAnalyzer();
            log.Service = mockWebService.Object;
            log.Email = mockEmailService.Object;
            log.Analyze("xxx");
            mockEmailService.Verify(p => p.SendEmail("[email protected]", "[email protected]", "IWebServiceFailed", It.Is<string>(x => x != null)));
        }
    }           

這樣我就完成了我的單元測試,而不用去關心我的依賴的代碼的實作。保證我的功能的正确性。

對上面Mock的說明如下:

第一個模拟LogError抛出異常的代碼:

mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws(new Exception());           

第一行,當參數類型是string且長度大于8時正常執行,而長度長于等于8時則抛出異常。他的另一種寫法是範型:

mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws<Exception>();           

在我調用分析方法Analyze時傳入的字元串不長于8個,就會完成異常抛出異常的功能。

第二個是Email接口的Mock對象,建立如下:

var a = mockEmailService.Setup(e => e.SendEmail("[email protected]", "[email protected]", "IWebServiceFailed", It.Is<string>(x=>x != null)));           

因為最後一個參數是異常的Message,是以我們需要動态指定。前三個參數和代碼中一緻。

最後驗證SendEmail有沒有執行。這行代碼不能放在log.Analyze調用之前。因為這個時候方法還沒有調用,單元測試不會通過。并且參數保持一緻。如果參數不一緻(特别是前三個)也會測試失敗。這就是Mock的強大之處。

你的支援是我繼續的動力啊。

【C#】【xUnit】【Moq】.NET單元測試Mock架構Moq初探!
【C#】【xUnit】【Moq】.NET單元測試Mock架構Moq初探!
【C#】【xUnit】【Moq】.NET單元測試Mock架構Moq初探!

繼續閱讀