Anshul
Anshul

Reputation: 33

test if a method is called by mocking another method using moq

Given a class that has method SplitList and Update. Update is being called from SplitList.

class BaseClass
{
    public void SplitList(ref IList<Type> objList)
    {
        IList<Type> SplitA = objList.Where(c=>c.currency == "USD").ToList();
        IList<Type> SplitB = objList.Where(c=>c.currency == "GBR").ToList();

        if(SplitA.Count() > 0)
        {
            Update(ref SplitA);
        }

        if(SplitB.Count() > 0)
        {
            Update(ref SplitB);
        }
    }
}

What i want to test is when I call SplitList method, how many times is Update being called. I mean if both if being called, or one, or none. Need to test the boundary conditions for that method.

How i have proceeded for it is,

class TestClass
{
    [TestMethod]
    void TestSplitList()
    {
        Mock<BaseClass> mock = new Mock<BaseClass>();
        mock.Setup(m=>m.Update(ref List)).Verifiable();
        mock.Object.SplitList(ref List);
        mock.Verify(m=>m.Update(ref List), Times.Exactly(1));
    }
}

This code is giving me error as, Expected invocation on the mock exactly 1 times, but was 0 times: m => m.Update(.List)

Can anyone please help with this ?

Upvotes: 3

Views: 1053

Answers (2)

StuartLC
StuartLC

Reputation: 107237

Mocking a class with a virtual method will allow you to verify the method. You can also use CallBase to invoke the actual method (Update) at the same time.

Assuming the following code (given that your posted Update does not contain a ref parameter, I've removed the unnecessary ref from Split):

public class Type
{
    public string currency { get; set; }
    public int OrderNo { get; set; }
}
public class BaseClass
{
    // w.r.t ref, do you mean to reassign objList to the filtered lists?
    public void SplitList(ref IList<Type> objList)
    {
        var SplitA = objList.Where(c => c.currency == "USD").ToList();
        var SplitB = objList.Where(c => c.currency == "GBR").ToList();

        if (SplitA.Count() > 0)
        {
            Update(SplitA);
        }

        if (SplitB.Count() > 0)
        {
            Update(SplitB);
        }
    }

    public virtual IList<Type> Update(IList<Type> updateList)
    {
        int count = 0; 
        foreach (Type objType in updateList)
        {
            objType.OrderNo = count++;
        } 
        return updateList;
    }
}

This Unit test demonstrates that the virtual Update method can be verified as well as being invoked. If you choose not to use CallBase, then the Unit Test will need to be adjusted as the mutation to the elements in the list will not happen.

[Test]
public void TestSplitList()
{
    var mock = new Mock<BaseClass>();
    mock.CallBase = true; // This will ensure the actual Update also gets called
    IList<Type> fakeTypes = new List<Type>
                        {
                            new Type {currency = "GBR"},
                            new Type {currency = "GBR", OrderNo = 100},
                            new Type {currency = "JPY", OrderNo = 55}
                        };

    mock.Object.SplitList(ref fakeTypes);

    mock.Verify(m => m.Update(It.IsAny<IList<Type>>()), Times.Exactly(1));
    mock.Verify(m => m.Update(It.Is<IList<Type>>(x => x.Any(y => y.currency == "GBR") 
                && x.Count == 2)), Times.Exactly(1));
    mock.Verify(m => m.Update(It.Is<IList<Type>>(
        x => x.Any(y => y.currency == "JPY"))), Times.Never);
    Assert.AreEqual(3, fakeTypes.Count, "List itself must not have changed");

    // These tests show the effect of the actual `Update` method
    Assert.IsTrue(fakeTypes.Any(t => t.OrderNo == 0 && t.currency == "GBR"), 
       "GBR must be ordered");
    Assert.IsTrue(fakeTypes.Any(t => t.OrderNo == 1 && t.currency == "GBR"), 
       "GBR must be ordered");
    Assert.IsFalse(fakeTypes.Any(t => t.OrderNo == 100 && t.currency == "GBR"), 
       "GBR must be ordered");
    Assert.IsTrue(fakeTypes.Any(t => t.OrderNo == 55 && t.currency == "JPY"), 
       "JPY must not be ordered");
}

Note: The use of ref on the List parameters is unnecessary in your current code implementation. Because you are directly mutating the objects in the list, these changes will be visible to all collections referencing these items. Also, you aren't changing the list itself (just the elements in it), so you won't need to either make the Update param ref, nor return it from Update.

Upvotes: 0

henginy
henginy

Reputation: 2071

Is your SplitList method virtual? (Since your posted code might be not identical to the codebase.) In that case, Moq will override it and your Update will not be called. You can either make it non-virtual, or have Moq call it by adding this line:

mock.CallBase = true;

If you choose this approach, beware that all methods will "CallBase" (if no expectation overrides the member).

Update: You're passing a different list to Update method in SplitList implementation. The parameter is objList, then you create a different lists (SplitA and SplitB) and pass one them to Update method. Since SplitA (or SplitB) != objList, the test fails.

Do you really have to use ref here? If you remove it, the code will be simpler and the tests will pass. The lists will still be different without ref, sorry I missed that. I think you might need to change your logic to allow better testing..

Upvotes: 1

Related Questions