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
Hello, I'd appreciate some advice on what's going wrong with my code (or expectations) here. I'm looking to introduce TypeMock into our development process, but am having some difficulties with the code below which is a cut-down version of our Production code.

What appears to be happening is that if I use a Isolate.WhenCalled() method with WithExactArguments() AFTER a previous Isolate.WhenCalled(), it seems to wipe out the expected value of the previous call.

In my example below I have two test cases. The first test case arranges that TransactionStatus will return EnumTranStatus.Validated. Then it arranges that the GetField() method with the parameter "Spread" returns a field object. However once the second WhenCalled() method is called, the TransactionStatus returns back to the default value.

In the second test case, I re-arrange the calls and it works as expected.
What is going wrong? I'd appreciate any help.

Here is the main test fixture (for the VS.NET test environment).

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TypeMock.ArrangeActAssert;
using TypeMock.Test.Code;

namespace TypeMock.Test
{
    [TestClass]
    public class TestFixture
    {
        private Field _spreadField;

        [TestInitialize]
        public void Initialise()
        {
            _spreadField = Isolate.Fake.Instance<Field>();
        }


        [TestMethod]
        public void WithExactArgumentsAfterFails()
        {
            var transactionInfo = Isolate.Fake.Instance<IPreprocessingInfo>();

            Isolate.WhenCalled(() => transactionInfo.Transaction.TransactionStatus).WillReturn(EnumTranStatus.Validated);
            // transactionInfo.Transaction.TransactionStatus now returns Validated as expected.

            Isolate.WhenCalled(() => transactionInfo.Transaction.GetField("Spread")).WithExactArguments().WillReturn(_spreadField);
            // Why is transactionInfo.Transaction.TransactionStatus now being set as the default of "None"?

            Assert.AreEqual(EnumTranStatus.Validated, transactionInfo.Transaction.TransactionStatus);
        }

        [TestMethod]
        public void WithExactArgumentsBeforeSucceeds()
        {
            var transactionInfo = Isolate.Fake.Instance<IPreprocessingInfo>();

            Isolate.WhenCalled(() => transactionInfo.Transaction.GetField("Spread")).WithExactArguments().WillReturn(_spreadField);
            Isolate.WhenCalled(() => transactionInfo.Transaction.TransactionStatus).WillReturn(EnumTranStatus.Validated);
            // This behaves as expected

            Assert.AreEqual(EnumTranStatus.Validated, transactionInfo.Transaction.TransactionStatus);
        }
    }
}


And here are the associated types:
Field:
namespace TypeMock.Test.Code
{
    public class Field
    {
    }
}


IPreprocessingInfo:
namespace TypeMock.Test.Code
{
    public interface IPreprocessingInfo
    {
        Transaction Transaction { get; }
    }
}


Transaction:
namespace TypeMock.Test.Code
{
    public class Transaction
    {
        public Field GetField(string name)
        {
            return new Field();
        }

        public EnumTranStatus TransactionStatus { get; set; }
    }

    public enum EnumTranStatus
    {
        None = 0,
        Validated = 1
    }
}


This happens in 6.0.6 and 6.1.2.
asked by AndyPUK (1.2k points)

3 Answers

0 votes
I've created a simpler example, if that helps.
It appears to be associated with the fact that it occurs when there is method chaining and the enumeration being checked is part of a nested class.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TypeMock.ArrangeActAssert;

namespace TypeMock.Test
{
    internal class MyClass
    {
        public MyClass()
        {
            Sub = new MySubclass();
        }

        public MySubclass Sub { get; private set;  }

        internal class MySubclass
        {
            public int Foo(int param)
            {
                return -1 * param;
            }

            public Status CurrentStatus { get; set; }
        }

        public enum Status
        {
            Ok = 1,
            Fail = 2
        }
    }

    [TestClass]
    public class TestFixture2
    {
        [TestMethod, Isolated]
        public void TestMethod1()
        {
            var myClass = Isolate.Fake.Instance<MyClass>();

            Isolate.WhenCalled(() => myClass.Sub.CurrentStatus).WillReturn(MyClass.Status.Fail);

            Isolate.WhenCalled(() => myClass.Sub.Foo(1)).WillReturn(-1);
            Assert.AreEqual(MyClass.Status.Fail, myClass.Sub.CurrentStatus);  // OK so far

            Isolate.WhenCalled(() => myClass.Sub.Foo(5)).WithExactArguments().WillReturn(-5);
            Assert.AreEqual(MyClass.Status.Fail, myClass.Sub.CurrentStatus);  // Fail!

            Isolate.WhenCalled(() => myClass.Sub.Foo(2)).WillReturn(-2);
        }
    }
}


and if I remove the nested class and do it again, the problem doesn't occur.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TypeMock.ArrangeActAssert;

namespace TypeMock.Test
{
    internal class MyClass2
    {
        public int Foo(int param)
        {
            return -1 * param;
        }

        public Status CurrentStatus { get; set; }

        public enum Status
        {
            Ok = 1,
            Fail = 2
        }
    }

    [TestClass]
    public class TestFixture3
    {
        [TestMethod, Isolated]
        public void TestMethod1()
        {
            var myClass = Isolate.Fake.Instance<MyClass2>();

            Isolate.WhenCalled(() => myClass.CurrentStatus).WillReturn(MyClass2.Status.Fail);

            Isolate.WhenCalled(() => myClass.Foo(1)).WillReturn(-1);
            Assert.AreEqual(MyClass2.Status.Fail, myClass.CurrentStatus);  // OK so far

            Isolate.WhenCalled(() => myClass.Foo(5)).WithExactArguments().WillReturn(-5);
            Assert.AreEqual(MyClass2.Status.Fail, myClass.CurrentStatus);  // This time it works!

            Isolate.WhenCalled(() => myClass.Foo(2)).WillReturn(-2);
        }
    }
}


Any thoughts? I'm trying to mock an API and currently I don't have the confidence to use TypeMock until I know why this is happening.
answered by AndyPUK (1.2k points)
0 votes
Hi Andy,

This is a bug, caused apparently because of the call chaining inside WhenCalled.

As a workaround, use local variables to store the chains before using WhenCalled.

For example, in your code
change the line:
Isolate.WhenCalled(() => TransactionInfo.Transaction.TransactionStatus).WillReturn(EnumTranStatus.Validated)

with the lines:
var t = TransactionInfo.Transaction;
Isolate.WhenCalled(() => t.TransactionStatus).WillReturn(EnumTranStatus.Validated);


We will try to get a fix for this into the next release.
answered by yoel (1.9k points)
0 votes
Yoel,

Thank you for your fast response.

I've modified my code and can confirm the workaround passes. I'll change my unit tests accordingly.

Thanks again

Andy
answered by AndyPUK (1.2k points)
...