Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I verify whether one of two methods has been called?

I am using Moq to verify if a method is being called in my unittest. In this specific case I want to test if the method under test logs an Error through log4net. The problem is, this can be done by either calling log.Error or log.ErrorFormat. Either is fine.

How can I verify this though? I only know how to verify that they have both been called.

var logMock = new Mock<ILog>();

var myClass = new MyClass(logMock.Object);

myClass.MyMethod();

logMock.Verify(log => log.Error(It.IsAny<object>()));
logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>()));

Now that I think of it, they both have a bunch of overloads, I don't mind if any of the overloads are called either (I'm starting to doubt this is a good test).

Thanks in advance.

EDIT: I just thought of something nasty:

try
{
    logMock.Verify(log => log.Error(It.IsAny<object>()));
}
catch (Moq.MockException ex)
{
    logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>()));
}

Maybe I can wrap this in some kind of extension method... e.g. VerifyAny.

like image 974
Matthijs Wessels Avatar asked Jan 10 '13 00:01

Matthijs Wessels


1 Answers

You could register a callback for each valid error method that sets a flag:

// Arrange
bool errorFlag = false;
logMock
    .Setup(l => l.Error(It.IsAny<object>()))
    .Callback((object o) => errorFlag = true);

/* repeat setup for each logMock method */

// Act
myClass.MyMethod();

// Assert
Assert.IsTrue(errorFlag);

Of course, this will still be tedious if you have many overloads to cover.

EDIT: And for fun, here's an extension method for Mock<T>.VerifyAny:

public static class MockExtensions
{
    public static void VerifyAny<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions)
        where T: class
    {
        List<MockException> exceptions = new List<MockException>();
        bool success = false;
        foreach (var expression in expressions)
        {
            try
            {
                mock.Verify(expression);
                success = true;
                break;
            }
            catch (MockException ex)
            {
                exceptions.Add(ex);
            }
        }

        if (!success)
        {
            throw new AggregateException("None of the specified methods were invoked.", exceptions);
        }
    }
}

Usage:

[TestMethod]
public void FooTest()
{
    Mock<IFoo> fooMock = new Mock<IFoo>();
    fooMock.Object.Bar1();

    fooMock.VerifyAny(
        f => f.Bar1(),
        f => f.Bar2());
}
like image 165
Scott Wegner Avatar answered Oct 13 '22 00:10

Scott Wegner