views:

307

answers:

3

Hi

I'm using TypeMock Isolater to mock up some objects for some unit tests - attempting to use the AAA api (so the Isolate calls).

I have a straightforward singleton class where you call a static GetInstance(), which then returns an instance of the class. I thought it would be a simple matter of mocking that up, but I'm running into a very frustrating problem ! I can't seem to make GetInstance() return my mocked object correctly with my expected calls set.

I've tried:

  • using MST projects (using the Accessor classes) to assign a mocked object directly to the instance variable (faking the object using Memers.MustSpecifyReturnValues, and Isolate.WhenCalled using WithExactArguments to set expectations), but for some reason the mocked object always returns null (and no exceptions).
  • Mocking Singleton.GetInstance() to return the mocked object. This returns a mocked object which needs WhenCalled set, but now the Isolate.WhenCalled calls I make seem to do nothing on the fake object - so all calls throw an unexpected call exception.
  • I've also tried mocking the actual method call (eg Singleton.GetInstance().Test()), which will work for the call to that method, but all other calls to other methods on the singleton return null rather then throw an exception as I want it to (because this seems to automatically mock up all the objects without Members.MustSpecifyReturnValues).

All I want is to mock a singleton, and any calls I don't explicitly tell it to expect to throw an exception on. I thought it would be simple, but apparently not ! Sad

Has anyone any idea what I'm doing wrong?

Thanks James

A: 

The best solution is to not use singletons (or any other static mutable data). Just create one instance and use dependency injection to pass it to all objects who need it.

Esko Luontola
+3  A: 

I think the simple solution will be to create a fake instance of the singleton class and use SwapNextInstace before the actual class constructor is called:

[TestMethod]
public void SetBhaciorOnSingleton()
{
   var fake = Isolate.Fake.Instance<SingletonClass>();

   Isolate.WhenCalled(() => fake.SomeFunction()).WillReturn(10);
   // Set additional behavior on singleton class

   Isolate.Swap.NextInstance<SingletonClass>().With(fake);

   // This is where the class constructor is being called
   var result = SingletonClass.GetInstace().SomeFunction();
   Assert.AreEqual(10, result );
}

This solution should work with most scenarios unless the singleton class is created before the test. If you need to set behavior after the class was created just use WhenCalled:

[TestMethod]
public void SetBhaciorOnSingleton()
{
    var fake = Isolate.Fake.Instance<SingletonClass>();

    Isolate.WhenCalled(() => fake.SomeFunction()).WillReturn(10);

    Isolate.WhenCalled(() => SingletonClass.GetInstace()).WillReturn(fake);

    var result = SingletonClass.GetInstace().SomeFunction();
    Assert.AreEqual(10, result );
}
Dror Helper
A: 

Thanks.

I didn't try NextInstance before because it doesn't work on interfaces which I didn't really want to change.

But, I've tried it and it does work - although I was assuming the order of setting WhenCalled(s) doesn't really matter, but it definately does. If I do the WhenCalled after the Swap for instance, it doesn't work. It needs to go before the Swap. (Doesn't really make sense to me to be honest - it should be the same object).

However, the last example (one of the ways I had tried), doesn't work for me. I fake, set expecation on fake, and then set expectation on Singleton to return faked instance - but now it returns the concrete instance !

Could it have something to do with the way the constructors are called? I remember seeing something about that...

Alternatively I could use the Swap, but, I wanted to be able to setup all this stuff in a TestSetup, and make minor modifications to the expectations in the actual test, but that doesn't look possible. ?

James D