我的业务逻辑在星期六或星期日没有执行某些功能。我希望我的单元测试能够验证这些功能是否已执行,但测试将在周六/周日失败。
我认为最简单的方法是让单元测试传达一条友好的信息,说明如果测试结果在周六/周日运行则无效。
使用C#/ NUnit ......
Assert.That(
DateTime.Now.DayOfWeek != DayOfWeek.Sunday && DateTime.Now.DayOfWeek !=
DayOfWeek.Saturday, "This test cannot be run on Saturday or Sunday");
尝试嘲笑约会是否可行/可取?是否有其他策略来处理这种情况?
答案 0 :(得分:9)
尝试模拟日期是否可行/可取?
是的,不仅是可行的,而且它是可靠地单独测试代码的唯一方法。假设您想测试以下(无意义)方法:
public bool Foo()
{
return (DateTime.Now.DayOfWeek == DayOfWeek.Sunday);
}
很明显,你无法测试它,因为它依赖于静态方法(静态Now属性更精确)。很明显,像这样紧密耦合的组件不能单独进行单元测试。
现在考虑这种改进(与构造函数DI分离关注点):
private readonly Func<DateTime> _nowProvider;
public SomeClass(Func<DateTime> nowProvider)
{
_nowProvider = nowProvider;
}
public bool Foo()
{
return (_nowProvider().DayOfWeek == DayOfWeek.Sunday);
}
现在,单元测试更好更容易。 SomeClass
不再依赖于非确定性日期。在实际应用程序中,您显然会像这样实例化SomeClass
:
var s = new SomeClass(() => DateTime.Now);
s.Foo();
在您的单元测试中,您将模拟它以便您可以验证这两种情况:
var subjectUnderTest = new SomeClass(() => new DateTime(2011, 1, 3));
var actual = subjectUnderTest.Foo();
// assertions, ...
答案 1 :(得分:2)
您应该在系统时钟上创建一个抽象,因为您应该创建其他系统资源和外部资源的抽象,以便能够编写RTM unit tests。
对系统时钟的抽象非常简单,看起来像这样:
public interface ISystemClock
{
DateTime Now { get; }
}
应用程序中依赖于系统时钟的类通常应该具有ISystemClock
作为构造函数参数。对于单元测试场景,您可以使用如下所示的ISystemClock
实现:
public class FakeSystemClock : ISystemClock
{
// Note the setter.
public DateTime Now { get; set; }
}
现在你可以在你的单元测试中使用这个假时钟:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException),
"Service should throw an exception when called on saturday.")]
public void DoSomething_WhenCalledOnSaturday_ThrowsException()
{
// Arrange
var saturday = new DateTime(2011, 1, 1);
Assert.AreEqual(saturday.DayOfWeek, DayOfWeek.Saturday,
"Test setup fail");
var clock = new FakeSystemClock() { Now = saturday };
var service = new Service(clock);
// Act
service.DoSomething();
}
在您的应用程序项目中,您可以定义实际使用系统时钟的ISystemClock
实现。使用给定的ISystemClock
定义,它将如下所示:
public class RealSystemClock : ISystemClock
{
public DateTime Now => DateTime.Now;
}
使用DI容器时,可以将其配置为连接类型,并在构造函数指定RealSystemClock
参数时自动注入ISystemClock
的实例。
顺便说一句。虽然您可以使用Func<DateTime>
委托来完成这一操作,但为此定义一个接口更加明确,并且对其他开发人员来说更具可读性。除此之外,将DI容器中的所有内容连接起来会容易得多,因为它很容易最终导致多个Func<DateTime>
依赖项,所有这些都与“给我当前的本地时间”不同。
我希望这会有所帮助。
答案 2 :(得分:0)
Noda Time 可能是新项目的解决方案/替代方案,因为它都是基于界面的,因此很容易模拟。