我知道我不能使用Moq在我测试的方法中模拟静态方法调用,那么我需要做什么来重构方法以便我可以测试它?我也有一个方法调用基类方法,我需要重构那个,如果是这样的话怎么样?我不想使用MS.Fakes或TypeMocks并创建一个垫片,我宁愿重构并编写可靠的代码!
public override DateTime ResolveDate(ISeries comparisonSeries, DateTime targetDate)
{
if (comparisonSeries == null)
{
throw new ArgumentNullException("comparisonSeries");
}
switch (comparisonSeries.Key)
{
case SeriesKey.R1:
case SeriesKey.R2:
case SeriesKey.R3:
case SeriesKey.R4:
case SeriesKey.R5:
return DateHelper.PreviousOrCurrentQuarterEnd(targetDate);
}
return base.ResolveDate(comparisonSeries, targetDate);
}
[TestMethod]
public void SomeTestMethod()
{
var mockIAppCache = new Mock<IAppCache>();
var mockISeries = new Mock<ISeries>();
ReportFR2 report = new ReportFR2(SeriesKey.FR2, mockIAppCache);
DateTime resolvedDate = report.ResolveDate(mockISeries, DateTime.Now);
//Assert.AreEqual("something", "something");
}
答案 0 :(得分:2)
综合以上所述,您可以测试三个基本条件:
当比较系列为空时
当比较系列键为R1:R5
当比较系列键不为空且除R1:R5
在条件1中,您可以非常轻松地进行测试。
在条件2中,当它是R1:R5时,它就是它出现的静态方法。
DateHelper.PreviousOrCurrentQuarterEnd()
函数,点击所有边缘情况。如果出现问题,这将有助于确定哪些代码是罪魁祸首。条件3作为条件2也可以这样说。虽然它在你的基类中,但它仍然是一个有效的逻辑分支。
同样,你很难证明它叫你的基类,
但检查结果仍然有效。
所以,我认为你可以编写四套测试来开始,然后在你完成这些传递之后,你可以决定是否要重构你的实用工具类DateHelper.
我的猜测是你我会说不:-D
给定ReportRF2类和null比较系列
调用report.ResolveDate
它应该抛出一个Null引用异常。使用
`Assert.Throws(()=&gt; report.ResolveDate(null,DateTime.Now));
给出一个ReportRF2类和一组R1中的系列键:R5
当解析边界X的日期(例如1/1/0001)时,它应该等于y;
当......的时候......,......; (重复边缘/边界情况;考虑使用数据驱动)
鉴于ReportRF2类和一系列键不在R1的集合中:R5和NOT NULL
给定静态实用程序类DateHelper
然后,这将为您提供预期结果的报道,并告诉您失败源于您的ResolveDate
方法或DateHelper.PreviousOrCurrentQuarterEnd()
方法。它可能不像纯粹主义者那样孤立,但只要你覆盖你的边缘情况和你快乐的路径,它就证明你的应用程序按计划运行(只要这些测试正在通过)。
它真正让你做的是断言除了比较系列为空时,采取了特定的行为,因此由你决定是否需要该验证。但是,您仍然应该证明,当某些值或范围进入时,您会得到可预测的输出,并且会增加一些价值。
答案 1 :(得分:1)
只是为了添加@ Damon的好答案,用界面包装DateHelper
可以很容易地完成:
public interface IDateHelper
{
DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate);
}
如上所述,您需要一个实现此接口的实例类,但仅适用于您的生产代码,因为单元测试只使用Mock<IDateHelper
:
public class InstanceDateHelper : IDateHelper
{
public DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate)
{
return DateTimeHelper.PreviousOrCurrentQuarterEnd(targetDate);
}
}
Voilà,您现在可以模拟IDateHelper
接口,并且您有一个使用现有静态代码的实现。
我已经使用这种包装技术在一个启动新Process
的方法上编写单元测试,所以我可以在没有实际启动完整过程的情况下测试该方法,而我需要知道的是测试中的方法会调用.Start(StartInfo)
,没有副作用。
想象一下这个方法:
public bool StartProcessAndWaitForExit(ProcessStartInfo info)
{
var process = Process.Start(info); // test-hindering static method call
//...
}
我必须做的唯一改变是:
public bool StartProcessAndWaitForExit(IProcessWrapper process, ProcessStartInfo info)
{
var process = process.Start(info); // injected wrapper interface makes method testable
//...
}
如果ResolveDate
是您班级中唯一需要IDateHelper
的方法,那么将其作为方法参数注入就可以了;如果你有一堆方法都需要它,将它作为构造函数参数注入并创建一个private readonly IDateHelper _helper;
字段(在构造函数中初始化)是最好的方法。