Matthijs Wessels
Matthijs Wessels

Reputation: 6739

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.

Upvotes: 13

Views: 2595

Answers (2)

Scott Wegner
Scott Wegner

Reputation: 7493

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());
}

Upvotes: 18

Bassam Mehanni
Bassam Mehanni

Reputation: 14944

if you are specifically testing that a specific error was logged, why not have 2 tests, one that ensure that log.Error is called and one that ensure that log.ErrorFormat is called, I am assuming that you can control which one is called based on the input.

if you still wanna verify one or the other, you can just use this approach, it does exactly what you need:

Verify that either one method or the other was invoked in a unit test

Upvotes: 1

Related Questions