NUnit 模拟不适用于单例方法

发布于 2024-08-07 23:57:38 字数 569 浏览 2 评论 0原文

请耐心等待,我是 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

Bear with me, I'm new to NUnit. I come from the land of Rails, so some of this is new to me.

I have a line of code that looks like this:

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

I'm trying to mock it, like this (assume code is already initialized):

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

When I debug the test, getCodeByCodeNameAndType is returning null, instead of the expected code. What am I doing wrong?

NUnit version: 2.2.8

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

望笑 2024-08-14 23:57:38

抱歉,我从未使用过 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);
}

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.消费接口

好了,所以步骤c。这是您大部分工作的地方。从逻辑上讲,如果您正在嘲笑您的单例,那么您实际上是在对消费者进行单元测试(您已将其排除在样本之外)。对于c。只需向使用者的构造函数添加一个参数,或者添加一个“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] 的实用介绍。这只是将服务引用(例如您的网站配置类)传递或“注入”给消费者的做法,而不是让消费者直接调用服务(例如通过静态单例类)。

希望这有帮助:)

I'm sorry, but I've never used NUnit.Mocks - but I do have some experience with NMock and Moq [which, by the way, I highly recommend]. Typically, you use a mocking library to generate proxies for Interface definitions, and I presume NUnit.Mocks operates the same way.

Therefore, if you would like to mock your singleton, you will likely have to do the following,

a. Create an interface, say

// 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. "Implement" interface

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

c. Consume interface

alright, so step c. is where most of your work will be. Logically, if you are mocking your singleton, you are actually unit testing the consumer [which you have left out of your sample]. For c. simply add a parameter to the consumer's ctor, or add a publicly accessible property of Type 'IWebSiteConfiguration', and then internally, reference the instance member and invoke your methods against this new interface. Consider this,

was

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

becomes

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

In your unit test, create the mock, pass a reference of the mock to the consumer, and test the consumer.

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

This also serves as a practical introduction to Dependency Injection [DI]. It's simply the practice of passing, or "injecting", references of services [eg your web site configuration class] to the consumer, rather than having the consumer invoke the service directly [eg via static singleton class].

Hope this helps :)

儭儭莪哋寶赑 2024-08-14 23:57:38

DynamicMock 在内存中创建一个新对象,该对象表示要模拟的接口或可编组(继承自 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,否则您所做的将会起作用。

A DynamicMock creates a new object in-memory that represents the interface, or marshallable (inherits from MarshalByRef) class you want to mock.

Try this:

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

Note that the third line there will not work unless WebSiteConfiguration inherits from MarshalByRef.

What you typically do is mock an interface and get a new object that implements this interface, but behaves the way you've configured it to do, without having to go and make a concrete type for it, so I'm not entirely sure what you're doing is going to work unless you employ a better isolation framework, like TypeMock that can intercept calls to static methods/properties in existing objects.

忆离笙 2024-08-14 23:57:38

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

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

Seems there is a kind of solution for this using reflection, or maybe I totally misunderstood this.

It is discussed here:
http://www.geekbeing.com/2010/05/23/how-to-unit-test-singleton-hack-in-c

Could it really works?

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

Project availabe on https://github.com/rbabreu/TestableSingleton

Actually I could not compile it on Visual Studio since the SingletonClass would have a private constructor. If someone get it to work would be great to avoid the overhead of adapter pattern.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文