簡體   English   中英

Moq單元測試C#類庫項目的最佳方法

[英]Best way to moq unit test c# class library project

SalaryManager類具有一個名為DoCalculation的方法,該方法調用使用工廠模式實現的GetSum方法。 除了調用GetSum方法外,DoCalculation方法還執行其他一些操作。 我想通過模擬對GetSum()的調用來對DoCalculation方法進行單元測試。 有人可以提出在Moq模擬中實現它的最佳方法嗎? 下面是示例代碼,

interface ICalc
{
int GetSum(int a, int b);
}

    class NormalCalc : ICalc
    {
        public int GetSum(int a,int b)
        {
            return a + b;
        }
    }
    class SumFactory
    {
        public static ICalc GetSumObject(int option)
        {
            if (option == 1)
                return new NormalCalc();
            return null;
        }
    }
    class SalaryManager
    {
        private static ICalc CalcRef = SumFactory.GetSumObject(1);

        public int DoCalculation(int a, int b)
        {
            int Sum=CalcRef.GetSum(a, b);
            //Perform some other operation
            //
            //
        }
    }

不要使用靜態工廠方法。 將工廠類轉換為可注入的服務/接口,並將其注入被測系統。

public interface ISumFactory {
    ICalc GetSumObject(int option);
}

public class SumFactory : ISumFactory {
    public  ICalc GetSumObject(int option) {
        if (option == 1)
            return new NormalCalc();
        return null;
    }
}
public class SalaryManager {
    private ICalc CalcRef;
    public SalaryManager(ISumFactory factory) {
        CalcRef = factory.GetSumObject(1);
    }

    public int DoCalculation(int a, int b) {
        int Sum = CalcRef.GetSum(a, b);
        //Perform some other operation
        //
        //
        //...;
    }
}

然后模擬依賴性進行測試並驗證期望。

[TestClass]
public class MyTestClass {
    [TestMethod]
    public void MyTestMethod() {
        //Arrange
        var calcMock = new Mock<ICalc>();
        calcMock.Setup(m => m.GetSum(It.IsAny<int>(), It.IsAny<int>()))
            .Returns((int a, int b) => a + b)
            .Verifiable();

        var factoryMock = new Mock<ISumFactory>();
        factoryMock.Setup(m => m.GetSumObject(1)).Returns(calcMock.Object)
        .Verifiable();

        var sut = new SalaryManager(factoryMock.Object);

        //Act
        var result = sut.DoCalculation(1, 1);

        //Assert
        //...
        factoryMock.Verify();
        calcMock.Verify();
    }
}

為了模擬GetSum方法,您將需要以某種方式將ICalc依賴項注入SalaryManager類。

盡管SalaryManager使用工廠創建ICalc並不能令人滿意,但工廠方法本身是返回具體類的靜態方法,您不能將其更改為返回模擬類,而是因為它是靜態方法,因此,根據這種設計,即使您的單元測試也必須使用NormalCalc作為Icalc實現。

您基本上有2個選擇:

  1. 通過其構造函數將ICalc直接注入SalaryManager:

     public class SalaryManager { private readonly ICalc _calc; public SalaryManager(ICalc clac) { _calc = calc; } public int DoCalculation(int a, int b) { int Sum = _calc.GetSum(a, b); //... } } 

    現在可以很容易地將模擬的ICalc實例注入您的類,只需使用moq創建一個ICalc的模擬並將其通過構造函數傳遞給calss。

  2. Anotehr選項,如果您仍想使用工廠(根據其用途,這在您的情況下似乎毫無用處,作為一個側面說明,僅當類依賴於我不依賴於IDisposable對象的情況下,我傾向於在使用DI時使用工廠想要在類的整個生命周期中保持生命)是將其從靜態方法更改為將實現接口的具體工廠:

     public interface ISumFactory { ICalc GetCalc(int option); } public SumFactory : ISumFactory { public ICalc GetCalc(int option) { if (option == 1) return new NormalCalc(); return null; } } 

    現在,您應該通過工廠構造函數將工廠接口注入到SalaryManager類中,並在需要時使用它:

     public class SalaryManager { private readonly ICalcFacotry _calcFactory; public SalaryManager(ICalcFacotry clacFacotry) { _calcFactory = clacFacotry; } public int DoCalculation(int a, int b) { ICalc calc = _calcFactory.GetCalc(1); int Sum = calc.GetSum(a, b); //... } } 

    現在,在單元測試中,您可以創建ICalcFacotry模擬並將其傳遞給您的類,當使用facotry方法(選項為1)時,應設置模擬的facotry以返回ICalc模擬。

您可以對該方法進行單元測試,而無需更改源代碼。
通過使用Typmock Isolator,您將能夠在類的未來實例創建之前對其進行模擬,因此,當您模擬Iclac的未來實例時,它將模擬實現該接口的所有類。

例如:

[TestMethod]
public void TestMethod()
{
    //mock the future instances of Iclac 
    //and when the next NormalClac will be created it will be mocked as well
    var fakeIclac = Isolate.Fake.NextInstance<ICalc>();

    //setting the behavior of GetSum
    Isolate.WhenCalled(() => fakeIclac.GetSum(0, 0)).WillReturn(5);

    var result = new SalaryManager().DoCalculation(0, 0);

    Assert.AreEqual(5, result);
} 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM