NUnit Mocking不适用于Singleton方法

时间:2009-10-21 14:11:26

标签: c# nunit mocking

忍受我,我是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

3 个答案:

答案 0 :(得分:7)

我很抱歉,但我从未使用过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。只需将一个参数添加到使用者的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)
    }
}

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

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

希望这会有所帮助:)

答案 1 :(得分:1)

DynamicMock在内存中创建一个新对象,表示要模拟的接口或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,否则完全确定你正在做的是什么。

答案 2 :(得分:1)

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

这里讨论: 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会有一个私有构造函数。如果有人让它工作将是很好的,以避免适配器模式的开销。