我在ViewModel中使用静态类(MyStaticClass
):
public class MyViewModel : ViewModelBase
{
public string MyProperty { get; set; }
//whatever...
public void FooMethod()
{
MyProperty = MyStaticClass.PropertyOne;
}
}
我的问题是:如何在MyStaticClass
单元测试中模拟MyViewModel
?
答案 0 :(得分:1)
我的问题是:如何在MyViewModel单元测试中模拟MyStaticClass?
您的问题的答案不仅仅是简单:
你不能。
一旦摆脱了代码中的静态垃圾,就可以将单元测试放回到表中。您甚至可以考虑单独测试应用程序的不同类,而不是它们的具体依赖项。所以在单元测试之前,花一点时间进行设计。是的,当然,你可以将$$$用在一些先进的单元测试框架上,它们会在运行时做一些巫术魔法,比如用一些编织代码替换你的程序集,注入东西等......,但老实说,从长远来看,花一些时间进行设计将是一种更有利可图的方法。
但是,除非您花了一些时间来适当地思考和设计代码,否则您可以采用一种廉价技巧来改善代码覆盖率:
public class MyViewModel : ViewModelBase
{
internal static Func<string> MyPropertyInjector = () => MyStaticClass.PropertyOne;
public string MyProperty { get; set; }
//whatever...
public void FooMethod()
{
this.MyProperty = MyPropertyInjector();
}
}
然后在unit test the friendly assembly
:
// arrange
MyViewModel.MyPropertyInjector = () => "some expected value";
// act
var sut = new MyViewModel();
sut.FooMethod();
// assert
Assert.AreEqual("some expected value", sut.MyProperty);
但正如我之前所说,这只是提高代码覆盖率的一个技巧,但你不应该误以为这个技巧可以提高你的代码质量。为此,您可能需要花更多时间思考和设计。
答案 1 :(得分:1)
我完全同意你应该避免使用静态类,如果你想进行单元测试,但是如果你对静态类没有选择权,那么为了单元测试可以控制它的行为(虽然严格来说)这不是嘲笑!)
例如,DateTime类包含非常有用的DateTime.Now静态方法,但在单元测试时总是很痛苦,因为&#34;现在&#34;总是在变化。为了解决这个问题,我创建了一个包装它的静态类;
public static class SystemTime
{
private static DateTime _date;
public static DateTime Now => _date != DateTime.MinValue ? _date : DateTime.Now;
public static DateTime Today => _date == DateTime.MinValue ? DateTime.Today : _date.Date;
[Conditional("DEBUG")]
public static void Set(DateTime date)
{
_date = date;
}
public static void Reset()
{
_date = DateTime.MinValue;
}
}
这是一个可以为单元测试操作的静态类的示例。在您的测试类设置方法中,您可以设置&#34; Set&#34; SystemTime使它始终返回您想要的值,使得测试代码调用当前时间可测试。您自己的静态类的重要部分是能够在&#34; test&#34;中设置静态类。状态,以便在单元测试中它以一种方式运行,但在发布代码中它的行为不同。
为此,您必须添加可以设置的属性或方法来更改代码的功能。但是有一个问题,它是静态的,所以一旦设置它就会保持不变。为了解决这个问题,你绝对必须记得将课程还原到它的生活&#34;状态,我会将此代码放在测试类的拆卸方法中,这样您就不会忘记重置它。另一个问题是,您引入的这些方法和属性可能会被非测试代码调用。为避免这种情况,您可以设置&#34; DEBUG&#34;属性或包装#if DEBUG块中的仅测试方法/属性。这并不能阻止您在开发过程中意外设置属性,但它会破坏发布版本并标记问题。小心使用这种方法!
编辑:
如果我有一个静态类,我不能创建非静态类,并且该类不仅仅是上面的简单示例,我将创建一个接口,然后为实现此接口的静态类创建一个包装器,同时也创建一个模拟类实现这个接口。我会在构造函数中传递该类(我更喜欢)或将其设置为属性。在测试中传递模拟版本,但在您的实际实现中传递包装版本。
答案 2 :(得分:0)
你不能模拟静态类。除了创建确定性函数之外,建议避免使用静态类。例如你传递这个值,你总是得到这个值 - 想想辅助函数等。
操纵数据或依赖某种状态的静态类非常难以测试。如果没有别的,你可以为静态类创建一个包装器,这将有助于缓解一些问题,但是你仍然可能遇到测试包装器类的问题。