3

I am trying to do a test delete using the Moq framework in C# and I am testing that the items were removed.

At the bottom, if I set int count to be events.Count, it returns 3. Brilliant!! However if i set it to be EventsMockDatabase.Count(), it returns 5 despite the fact that the mock has been setup to return events.Count.

Can anyone help me explain why?

    [Test]
    public void DeleteDuplicateEventDetailsFromRows()
    {
        var event1 = new EventLogObj() { RecordId = 1, TableKey = "PERSON_CODE=1", Status = "S" };
        var event2 = new EventLogObj() { RecordId = 2, TableKey = "PERSON_CODE=2", Status = "S" };
        var event3 = new EventLogObj() { RecordId = 3, TableKey = "PERSON_CODE=3", Status = "S" };
        var event4 = new EventLogObj() { RecordId = 4, TableKey = "PERSON_CODE=2", Status = "S" };
        var event5 = new EventLogObj() { RecordId = 5, TableKey = "PERSON_CODE=1", Status = "S" };

        var events = new List<EventLogObj>() { event1, event2, event3, event4, event5 };

        var mockEventsRepository = new Mock<IEventRepository>();

        mockEventsRepository.Setup(ev => ev.GetEvents())
            .Returns(events);

        mockEventsRepository.Setup(ev => ev.DeleteEvent(It.IsAny<decimal>()))
            .Callback((decimal RecID) =>
            {
                events.RemoveAll(e => e.RecordId == RecID);
            });

        mockEventsRepository.Setup(ev => ev.Count())              
            .Returns(events.Count);

        IEventRepository EventsMockDatabase = mockEventsRepository.Object;

        var eventLogObjects = new List<EventLogObj>();
        var duplicateEventLogObjects = new List<EventLogObj>();

        foreach (EventLogObj elo in EventsMockDatabase.GetEvents())
        {
            var existing = eventLogObjects.Where(
                e => e.TableKey.Equals(elo.TableKey)
            ).ToList();

            if (existing.Count == 0)
            {
                eventLogObjects.Add(elo);
            }
            else
            {
                duplicateEventLogObjects.Add(elo);
            }
        }

        Assert.AreEqual(2, duplicateEventLogObjects.Count);

        foreach (EventLogObj e in duplicateEventLogObjects)
        {
            // delete by id
            EventsMockDatabase.DeleteEvent(e.RecordId);
        }

        int count = events.Count; // EventsMockDatabase.Count(); // WHY!!!?

        Assert.AreEqual(3, count);
    }
Andy
  • 2,124
  • 1
  • 26
  • 29

1 Answers1

2

The value that is returned is stored when you Setup the Mock, so in this instance, the size of the initial list (5).

You can get round it by instead setting up the mock with a delegate:

mockEventsRepository.Setup(ev => ev.Count())
    .Returns(() => { return events.Count; });

As an aside, you do know that your current test wouldn't test any production code, it's simply testing if you've set your mock up to simulate a repository?

forsvarir
  • 10,749
  • 6
  • 46
  • 77
  • Brilliant! That works. Thanks - and yes I know it wont work on production. This is just to define the steps I intend to take for my live dataset. – Andy Jun 23 '15 at 09:24
  • 1
    I think the point @forsvarir makes here bears repeating: you should rewrite this test as it is currently only testing itself and not actual code. Generally when you use fakes/stubs/mocks/watever you want to call it using a mocking framework, you don't provide an implementation (like the list of events in your example). Rather, I usually verify whether the correct messages were sent to mocked collaborators. So instead of managing a list of events in your tests, just check that the delete event method was called correctly. If this requires GetEvents to return something, just return dummy data. – prgmtc Jun 23 '15 at 12:35
  • Ok I was using TDD to verify the steps I take when working on live data will work and really want to understand mocks. So @prgmtc you are suggesting i can just check the delete method is called correctly. The way I would do this is return a count of the data source is less after than before. All I have to do is translate that into code now!!! Any tips/samples? – Andy Jun 26 '15 at 18:51
  • @Ozzy I believe the point is you don't need to simulate the real behaviour with your mocks. What's important is that your code under test calls out to say `DeleteEvent` on your mock, passing in any relevant parameters. Have a look at things like http://stackoverflow.com/a/347907/592182 – forsvarir Jun 26 '15 at 19:06