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
//
//
}
}
答案 0 :(得分:1)
不要使用静态工厂方法。将工厂类转换为可注入服务/接口,并将其注入被测系统。
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();
}
}
答案 1 :(得分:1)
为了模拟GetSum方法,你需要以某种方式将ICalc依赖注入SalaryManager类。
虽然SalaryManager使用工厂创建ICalc而不是它本身不够好,但工厂方法本身是静态方法,它返回一个具体的类,你不能改变它来返回模拟类而不是因为它是一种静态方法,因此根据这种设计,即使你的单元测试也必须使用NormalCalc作为Icalc实现。
您基本上有两种选择:
通过它的构造函数将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选项,如果您仍想使用工厂(似乎 根据它的作用,在你的情况下相当无用,作为旁注,我倾向于在使用DI时使用工厂,只有当课程依赖于我不想在课堂上保持活力的IDisposable对象时才会使用工厂。整个生命时间)是改变 它从静态方法到将要实现的具体工厂 界面:
public interface ISumFactory
{
ICalc GetCalc(int option);
}
public SumFactory : ISumFactory
{
public ICalc GetCalc(int option)
{
if (option == 1)
return new NormalCalc();
return null;
}
}
现在您应该将工厂interfce注入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以在使用facotry方法时返回ICalc模拟,并选择1作为选项。
答案 2 :(得分:1)
您可以在不更改源代码的情况下对此方法进行单元测试
通过使用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);
}