C# NUnit模拟不适用于Singleton方法

C# NUnit模拟不适用于Singleton方法,c#,nunit,mocking,C#,Nunit,Mocking,请容忍我,我是新来的。我来自Rails之地,所以有些对我来说是新的 我有一行代码如下所示: var code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog); 我试图模拟它,就像这样(假设code已经初始化): 当我调试测试时,getCodeByCodeNameAndType返回null,而不是预期的code。我做错了什么 NUnit版本:2.2.8D

请容忍我,我是新来的。我来自Rails之地,所以有些对我来说是新的

我有一行代码如下所示:

var code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog);
我试图模拟它,就像这样(假设
code
已经初始化):

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


NUnit版本:2.2.8

DynamicLock在内存中创建一个新对象,该对象表示要模拟的接口或marshallable(从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);
请注意,除非WebSiteConfiguration继承自MarshalByRef,否则第三行将不起作用


您通常会模拟一个接口并获得一个实现该接口的新对象,但其行为方式与您配置的方式相同,无需为其创建具体类型,因此我不能完全确定您所做的工作是否有效,除非您采用更好的隔离框架,类似于TypeMock,它可以拦截对现有对象中静态方法/属性的调用。

很抱歉,我从未使用过NUnit.mock-但我确实有一些NMock和Moq方面的经验[顺便说一句,我强烈推荐]。通常,您使用模拟库为接口定义生成代理,我假定NUnit.Mocks的操作方式相同

因此,如果你想嘲笑你的单身汉,你可能需要做以下几点:

a。比如说,创建一个接口

// 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);
}
b。“实现”接口

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

// 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。只需向使用者的ctor添加一个参数,或添加一个类型为“IWebSiteConfiguration”的公共可访问属性,然后在内部引用实例成员并针对这个新接口调用您的方法。考虑这个,

变成

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)
    }
}
在单元测试中,创建mock,将mock的引用传递给使用者,然后测试使用者

[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
}

这也是对依赖注入[DI]的实用介绍。它只是将服务的引用传递或“注入”给使用者,而不是让使用者直接调用服务(例如通过静态单例类)


希望这有帮助:)

似乎有一种使用反射的解决方案,或者我完全误解了这一点

这里讨论的是:

这真的有效吗

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


实际上,我无法在VisualStudio上编译它,因为SingletonClass将有一个私有构造函数。如果有人能让它工作起来,避免适配器模式的开销将是非常好的。

如果这能激发你的食欲,请提供一些快速参考,以供进一步阅读。Martin Fowler关于依赖注入[DI]、控制反转[IoC]和容器[]关于Castle Windsor容器的快速教程[]
public class TestableSingleton : SingletonClass
{
  public TestableSingleton ()
  {
    FieldInfo fieldInfo = typeof(SingletonClass)
        .GetField("_instance",
        BindingFlags.Static | BindingFlags.NonPublic);
    fieldInfo.SetValue(Instance, this);
  }
}