Search code examples
c#unit-testingjustmock

How can I mock multiple instances of a struct?


I have a struct that I want to mock. In a more complex test I need several instances of this struct, each with it's own behavior. To facilitate this, I've created a helper method.

private MyStruct CreateMock(string toString) {
    var mock = Mock.Create<MyStruct>();
    Mock.Arrange(() => mock.toString()).Returns(toString);
    return mock;
}

When I debug a test where this method is called multiple times, it appears as if the Arrange call is overwritten for ALL instances of the struct (or maybe I am using struct mocking instead of instance mocking?).

I've tried:

mock.Arrange(m => m.toString()).Returns(toString); // Using Helpers assembly, note the lowercase m at the start of the line.

But to no avail. How can I get multiple instances of a struct?

I'm using: Microsoft Visual Studio Enterprise 2017 Version 15.9.17 VisualStudio.15.Release/15.9.17+28307.905 Microsoft .NET Framework Version 4.8.03761

Installed Version: Enterprise

JustMock 2020.1.219.1 Telerik JustMock Extension.

Example added:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Telerik.JustMock;
using Telerik.JustMock.Helpers;

namespace JustMockFramework
{
    public struct MyStruct
    {
        public readonly string Id;

        public MyStruct(string id)
        {
            Id = id;
        }

        public new string ToString()
        {
            return "Never read me!";
        }
    }

    [TestClass]
    public class MWE
    {
        [TestMethod]
        public void TestSimpleStruct()
        {
            var simpleTest = new MyStruct("1");

            Assert.AreEqual("Never read me!", simpleTest.ToString());
        }

        [TestMethod]
        public void TestMockOfStruct()
        {
            var mock = Mock.Create<MyStruct>();
            Mock.Arrange(() => mock.ToString()).Returns("Read me!");

            Assert.AreEqual("Read me!", mock.ToString());
        }

        [TestMethod]
        public void TestTwoMocksOfStruct()
        {
            var firstMock = Mock.Create<MyStruct>();
            Mock.Arrange(() => firstMock.ToString()).Returns("Read me!");

            var secondMock = Mock.Create<MyStruct>();
            Mock.Arrange(() => secondMock.ToString()).Returns("Read me too!");

            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }

        [TestMethod]
        public void TestTwoMocksOfStructCreatedInHelper()
        {
            var firstMock = CreateMockOfStruct("Read me!");

            var secondMock = CreateMockOfStruct("Read me too!");

            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }

        private MyStruct CreateMockOfStruct(string toString)
        {
            var mock = Mock.Create<MyStruct>();
            Mock.Arrange(() => mock.ToString()).Returns(toString);
            return mock;
        }

        [TestMethod]
        public void TestTwoMocksOfStructCreatedInHelperWithHelper()
        {
            var firstMock = CreateMockOfStructWithHelper("Read me!");

            var secondMock = CreateMockOfStructWithHelper("Read me too!");

            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }

        private MyStruct CreateMockOfStructWithHelper(string toString)
        {
            var mock = Mock.Create<MyStruct>();
            mock.Arrange((m) => m.ToString()).Returns(toString);
            return mock;
        }
    }
}

Edit: Cross posted

I've cross posted this question on the Telerik JustMock forum

Edit: License extended

My trial license was graciously extended. I've updated the answer to reflect this.


Solution

  • As described in the question, I cross posted the question and got an answer there.

    It all boils down to value VS reference comparisons. I was assuming a reference comparison but a value comparison is used. When actually passing an id to the creation of the mock, it should work.

    For convenience a copy of the proposed solution by Telerik.

    public struct MyStruct
    {
        public readonly string Id;
    
        public MyStruct(string id)
        {
            Id = id;
        }
    
        public override string ToString()
        {
            return "Never read me!";
        }
    }
    
    [TestClass]
    public class MWE
    {
        [TestMethod]
        public void TestSimpleStruct()
        {
            var simpleTest = new MyStruct("1");
    
            Assert.AreEqual("Never read me!", simpleTest.ToString());
        }
    
        [TestMethod]
        public void TestMockOfStruct()
        {
            var mock = Mock.Create<MyStruct>("1");
            Mock.Arrange(() => mock.ToString()).Returns("Read me!");
    
            Assert.AreEqual("Read me!", mock.ToString());
        }
    
        [TestMethod]
        public void TestTwoMocksOfStruct()
        {
            var firstMock = Mock.Create<MyStruct>("1");
            Mock.Arrange(() => firstMock.ToString()).Returns("Read me!");
    
            var secondMock = Mock.Create<MyStruct>("2");
            Mock.Arrange(() => secondMock.ToString()).Returns("Read me too!");
    
            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }
    
        [TestMethod]
        public void TestTwoMocksOfStructCreatedInHelper()
        {
            var firstMock = CreateMockOfStruct("1", "Read me!");
            var secondMock = CreateMockOfStruct("2", "Read me too!");
    
            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }
    
        private MyStruct CreateMockOfStruct(string id, string toString)
        {
            var mock = Mock.Create<MyStruct>(id);
            Mock.Arrange(() => mock.ToString()).Returns(toString);
            return mock;
        }
    
        [TestMethod]
        public void TestTwoMocksOfStructCreatedInHelperWithHelper()
        {
            var firstMock = CreateMockOfStructWithHelper("1", "Read me!");
            var secondMock = CreateMockOfStructWithHelper("2", "Read me too!");
    
            Assert.AreEqual("Read me!", firstMock.ToString()); // Fails with "Read me too!"
            Assert.AreEqual("Read me too!", secondMock.ToString());
        }
    
        private MyStruct CreateMockOfStructWithHelper(string id, string toString)
        {
            var mock = Mock.Create<MyStruct>(id);
            mock.Arrange((m) => m.ToString()).Returns(toString);
            return mock;
        }
    }