NUnit Mocking not working for Singleton Method

前端 未结 3 783
耶瑟儿~
耶瑟儿~ 2020-12-20 00:31

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 =         


        
相关标签:
3条回答
  • 2020-12-20 01:23

    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.

    0 讨论(0)
  • 2020-12-20 01:25

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

    0 讨论(0)
  • 2020-12-20 01:31

    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.

    0 讨论(0)
提交回复
热议问题