chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
Hi,

I'm trying to create a wrapper class that will wrap a mock and perform some boilerplate functions that I find myself repeating a lot. However, I'm finding that creating a mock inside of such a class causes the default behaviour (when no WhenCalled specified) to revert back to Members.CallOriginal.

I'm just wondering is there a reason for this? And is there a workaround?

Some demonstrative code is below. _InlineMock performs the mocking within the method. _WrapperSuppliedMock performs the same mocking from a wrapper class (this is the one which fails). Finally, _WrapperSuppliedMock performs the same mocking from a static method.



    [TestFixture][Isolated]
    internal class TestMyUnit
    {
        [Test]
        public void BuzzMyFizz_InlineMock()
        {
            var fizz = Isolate.Fake.Instance<Fizz>();
            Isolate.WhenCalled(() => fizz.OtherMethod()).DoInstead(c => 0);
            fizz.OtherMethod();
            fizz.Buzz();
        }

        [Test]
        public void BuzzMyFizz_WrapperSuppliedMock()
        {
            var fizzMock = new FizzMockWrap();
            fizzMock.Mock.OtherMethod();
            fizzMock.Mock.Buzz();
        }

        [Test]
        public void BuzzMyFizz_Static()
        {
            var fizzMock = FizzMockWrap.MakeFakeStatic();
            fizzMock.OtherMethod();
            fizzMock.Buzz();
        }
    }


    internal class Fizz
    {
        public void Buzz()
        {
            Console.WriteLine("Buzz");
            throw new Exception("Shouldn't get here");
        }

        public int OtherMethod()
        {
            throw new Exception("Shouldn't get here");
        }
    }

    internal class FizzMockWrap
    {
        public Fizz Mock { get; private set; }

        public FizzMockWrap()
        {
            Mock = Isolate.Fake.Instance<Fizz>();
            Isolate.WhenCalled(() => Mock.OtherMethod()).DoInstead(c => 0); // uncomment this line
        }

        public static Fizz MakeFakeStatic()
        {
            var Mock = Isolate.Fake.Instance<Fizz>();
            Isolate.WhenCalled(() => Mock.OtherMethod()).DoInstead(c => 0);
            Isolate.Swap.NextInstance<Fizz>().With(Mock);
            return Mock;
        }
    }

asked by adante (1.7k points)

3 Answers

0 votes
Hi,

This is an interesting approach, and you stumbled on a limitation in Isolator along the way; as part of Isolator's internal implementation, when you perform a WhenCalled() on a call chain that begins with a live (i.e. non faked) object, fakes are created along the call chain that behave like the real objects. This means that when you performed:
Isolate.WhenCalled(() => Mock.OtherMethod()...

It caused the getter of the Mock property, which is defined on an object that's not faked (FizzMockWrap) to return a CallOriginal fake, which interfered with the behavior you expected from Buzz() (to return recursive fakes).

Working around this is easy - replace the FizzMockWrap implementation by accessing a field rather than a property. This way we don't override the field's behavior:
    internal class FizzMockWrap
    {
        private Fizz mock;
        public Fizz Mock { get { return mock; } }

        public FizzMockWrap()
        {
            mock = Isolate.Fake.Instance<Fizz>();
            Isolate.WhenCalled(() => mock.OtherMethod()).DoInstead(c => 0); 
        }

        public static Fizz MakeFakeStatic()
        {
            var staticMock = Isolate.Fake.Instance<Fizz>();
            Isolate.WhenCalled(() => staticMock.OtherMethod()).DoInstead(c => 0);
            Isolate.Swap.NextInstance<Fizz>().With(staticMock);
            return staticMock;
        }
    }


Please let me know if this workaround works for you. I will look into fixing the original issue and avoid overriding the fake instance.

Thanks,
Doron
Typemock Support
answered by doron (17.2k points)
0 votes
Another thing I noticed in your tests is heavy use of DoInstead(); as a shortcut, and for readability, you can use WillReturn() in the situation you describe. Instead of this:
Isolate.WhenCalled(() => mock.Buzz()).DoInstead(c => 0);

you can use:
Isolate.WhenCalled(() => mock.Buzz()).WillReturn(0);


Doron
answered by doron (17.2k points)
0 votes
Hi,

This is an interesting approach, and you stumbled on a limitation in Isolator along the way; ...

Working around this is easy - replace the FizzMockWrap implementation by accessing a field rather than a property. This way we don't override the field's behavior:

Please let me know if this workaround works for you. I will look into fixing the original issue and avoid overriding the fake instance.

Thanks,
Doron
Typemock Support


Thanks for the prompt and detailed reply Doron. I'll mull over the explanation a little longer :) but the fix you suggested works perfectly.

Also appreciate the extra tip regarding WillReturn(). I actually use the above wrapper class to provide some convenience methods for simulating autoproperties (this is probably abuse of mocking) in this form:

class Wrapper
{
   public int SomeProperty { get; set; }

...

   public Wrapper()
{
   Isolate.WhenCalled(() => mock.SomeProperty).DoInstead(c => SomeProperty);
   Isolate.WhenCalled(() => mock.SomeProperty = 0).DoInstead(c => SomeProperty = (int)c.Parameters[0]);
}


Which is why I got into the habit DoInstead so much. Good to be reminded there are simpler ways from time to time.
answered by adante (1.7k points)
...