[英]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個選擇:
通過其構造函數將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。
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.