Search code examples
c#.netunit-testingtelerikjustmock

Trouble with Mock.Assert() for sequential calls with different argument values to mock


Could someone please take a look at the demo code below and let me know if what I'm seeing is due to error on my part or a Telerik issue?

I'm using Telerik.JustMock v. 2014.1.1519.1. and Microsoft.VisualStudio.QualityTools.UnitTestFramework v. 10.0.0.0.

As the code comments note, I get the expected results when the id variables are equal (one call for each of the ids), but not when they're different. When I step through the first test I can see the expected calls being made, but JustMock then tells me they weren't made.

I'll appreciate any constructive thoughts. Hopefully this isn't a case of me not getting enough sleep...

[TestClass]
public class RunnerTests
{
[TestMethod]
public void MakeTwoCallsDifferentIdsFails()
{
    int idOne=1;
    int idTwo=2;

    DataTable dt=new DataTable();
    dt.Columns.Add("Id");
    dt.Rows.Add(idOne);
    dt.Rows.Add(idTwo);

    IProcessor mock = Mock.Create<IProcessor>();
    Runner runner = new Runner(mock);
    runner.Process(dt);

    Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
    //The following two asserts fail (with 0 calls made to mock), regardless of sequence:
    Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>     
       (d=>d.Id==idOne)),Occurs.Once());
    Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
       (d=>d.Id==idTwo)),Occurs.Once());
}

[TestMethod]
public void MakeTwoCallsSameIdPasses()
{
    //ids intentionally equal:
    int idOne=1;
    int idTwo=1;

    DataTable dt=new DataTable();
    dt.Columns.Add("Id");
    dt.Rows.Add(idOne);
    dt.Rows.Add(idTwo);

    IProcessor mock = Mock.Create<IProcessor>();
    Runner runner = new Runner(mock);
    runner.Process(dt);

    //all asserts pass:
    Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
    //The following two pass:
    Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>     
        (d=>d.Id==idOne)),Occurs.Exactly(2));
    Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
        (d=>d.Id==idTwo)),Occurs.Exactly(2));
}
}

public interface IProcessor
{
    void Process(MyArgs args);
}

public class MyArgs
{
    public void UpdateId(int newId)
    {
        this.Id = newId;
    }

    public int Id {get; private set;}
}

public class Runner
{
    private IProcessor processor;

    public Runner(IProcessor processor)
    {
        this.processor=processor;
    }

    public void Process(DataTable dt)
    {
        MyArgs args = new MyArgs();

        foreach(DataRow row in dt.Rows)
        {
            int id = Convert.ToInt32(row["Id"]);
            args.UpdateId(id);
            processor.Process(args);
        }
    }
}

EDIT: In the test method that fails, if I completely remove one of the int variables and explicitly assert that the other was called exactly once, the test passes. Things seem to go south only when I throw that second, different value into the mix.


Solution

  • Since I couldn't get our mocking framework of choice (JustMock) to accomplish what I wanted, I ended up applying KISS, forgot about the mocking framework for this test, and went with something like the following:

    I changed my test method to look like this:

        [TestMethod]
        public void TwoCallsDifferentIds()
        {
            int idOne = 1;
            int idTwo = 2;
    
            DataTable dt = new DataTable();
            dt.Columns.Add("Id");
            dt.Rows.Add(idOne);
            dt.Rows.Add(idTwo);
            FakeProcessor processor = new FakeProcessor();
            Runner runner = new Runner(processor);
            runner.Process(dt);
    
            Assert.AreEqual(2, processor.MyArgsIds.Count);
            Assert.AreEqual(1, processor.MyArgsIds[0]);
            Assert.AreEqual(2, processor.MyArgsIds[1]);
        }
    

    and added the following fake:

    public class FakeProcessor : IProcessor
    {
        private IList<int> mList = new List<int>();
    
        public IList<int> MyArgsIds 
        {
            get { return mList; }
        }
    
        public void Process(MyArgs args)
        {
            mList.Add(args.Id);
        }
    }
    

    Not as slick, perhaps, but it got me the test I wanted.