NUnit Mocking不适用于Singleton方法

忍受我,我是NUnit的新手。 我来自Rails之地,所以其中一些对我来说是新的。

我有一行代码如下:

var code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog); 

我试图模仿它,就像这样(假设code已经初始化):

 var _websiteConfigurationMock = new DynamicMock(typeof(WebSiteConfiguration)); _websiteConfigurationMock.ExpectAndReturn("getCodeByCodeNameAndType", code); 

当我调试测试时, getCodeByCodeNameAndType返回null ,而不是预期的code 。 我究竟做错了什么?

NUnit版本:2.2.8

DynamicMock在内存中创建一个新对象,该对象表示要模拟的接口或marshallable(inheritance自MarshalByRef)类。

试试这个:

 var _websiteConfigurationMock = new DynamicMock(typeof(WebSiteConfiguration)); _websiteConfigurationMock.ExpectAndReturn("getCodeByCodeNameAndType", code); WebSiteConfiguration conf = (WebSiteConfiguration)_websiteConfigurationMock.MockInstance; var x = conf.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog); 

请注意,除非WebSiteConfigurationinheritanceMarshalByRef,否则第三行将无效。

你通常做的是模拟一个接口并获得一个实现这个接口的新对象,但是按照你配置它的方式行事,而不必为它做一个具体的类型,所以我不完全确定是什么除非你使用更好的隔离框架,比如可以拦截对现有对象中的静态方法/属性的调用的TypeMock,你所做的就是工作。

对不起,我从未使用过NUnit.Mocks – 但我确实对NMock和Moq有一些经验[顺便说一下,我强烈推荐]。 通常,您使用模拟库来生成接口定义的代理,并且我假设NUnit.Mocks以相同的方式操作。

因此,如果你想嘲笑你的单身人士,你可能需要做以下事情,

一个。 比如创建一个界面

 // All methods you would like to mock from this class, should // be members of this interface public interface IWebSiteConfiguration { // Should match signature of method you are mocking CodeType getCodeByCodeNameAndType ( string codeString, CatalogType catalogType); } 

湾 “实施”界面

 // You've already written the method, interface matches signature, // should be as easy as slapping interface on class declaration public class WebSiteConfiguration : IWebSiteConfiguration { } 

C。 消费界面

好吧,所以步骤c。 是你大部分工作的地方。 从逻辑上讲,如果你在嘲笑你的单身人士,你实际上是对消费者进行unit testing[你从样本中遗漏了]。 对于c。 只需将一个参数添加到使用者的ctor中,或者添加Type’IWebSiteConfiguration’的可公开访问的属性,然后在内部引用实例成员并针对此新接口调用您的方法。 想想这个,

 public class MyClass { public MyClass () { } public void DoSomething () { // bad singleton! bad boy! static references are bad! you // can't change them! convenient but bad! code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType ( "some.string", someCatalog) } } 

 public class MyClass { private readonly IWebSiteConfiguration _config = null; // just so you don't break any other code, you can default // to your static singleton on a default ctor public MyClass () : this (WebSiteConfiguration.Instance) { } // new constructor permits you to swap in any implementation // including your mock! public MyClass (IWebSiteConfiguration config) { _config = config; } public void DoSomething () { // huzzah! code = _config.getCodeByCodeNameAndType ("some.string", someCatalog) } } 

在unit testing中,创建模拟,将模拟的引用传递给使用者,并测试使用者。

 [Test] public void Test () { IWebSiteConfiguration mockConfig = null; // setup mock instance and expectation via // NUnit.Mocks, NMock, or Moq MyClass myClass = new MyClass (mockConfig); myClass.DoSomething (); // verify results } 

这也是dependency injection[DI]的实用介绍。 这只是向服务提供者传递或“注入”服务引用[例如您的网站配置类]的做法,而不是让消费者直接调用服务[例如通过静态单例类]。

希望这可以帮助 :)

似乎有一种使用reflection的解决方案,或者我可能完全误解了这一点。

这里讨论: http : //www.geekbeing.com/2010/05/23/how-to-unit-test-singleton-hack-in-c

真的有效吗?

 public class TestableSingleton : SingletonClass { public TestableSingleton () { FieldInfo fieldInfo = typeof(SingletonClass) .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic); fieldInfo.SetValue(Instance, this); } } 

项目可在https://github.com/rbabreu/TestableSingleton上获得

实际上我无法在Visual Studio上编译它,因为SingletonClass会有一个私有构造函数。 如果有人得到它上class将是很好的避免适配器模式的开销。