g.pickardou
g.pickardou

Reputation: 35933

How to check any of multiple overloads called NSubstitute

Scenario

I would like to check if a component (the sut) logs error in a particular condition. The ILogger interface constructor injected into the component, and the Error method has 4 overloads.

So I create a ILogger mock in the Arrange and using it in the Act.

I should not expect which overload the sut is using, just would like to expect and check if any of the overload called. (that would extremely white-box, and expects far more than the functional spec.)

Question

Currently my conclusion is that I can not utilize the .Received instead I must install callbacks for all the 4 overloads, and set a variable inside them, and in the Assert part I examine that variable.

Is any simple way to do this what I missed?

(example)

[TestMethod]
public void ShouldLogErrorIfEmailIsInvalid2()
{
    // Arrange
    var testEmailAddress = "dummy";

    //var mock = new Mock<IEMailValidator>();
    var validator = Substitute.For<IEMailValidator>();
    validator.Validate(Arg.Any<string>()).Returns(false);
    var logger = Substitute.For<ILogger>();

    var sut = new CustomerController(validator, logger);

    var customer = new Customer() { Email = testEmailAddress };

    // Act
    sut.Post(customer);

    // Assert
    // *** Here I do not want to expect a specific overload of Error, instead any of the 4 overloads satisfies the expectation
    logger.Received(1).Error(Arg.Is<string>( m => m.ToLower().Contains("email")), Arg.Any<object>());
}

Upvotes: 3

Views: 1004

Answers (1)

David Tchepak
David Tchepak

Reputation: 10484

NSubstitute does not have built-in syntax for this, but it is possible to query all ReceivedCalls() and manually assert on this.

For example:

var errorCalls = logger.ReceivedCalls()
    .Where(x => x.GetMethodInfo().Name == nameof(logger.Error))
    .Where(x => (x.GetArguments()[0] as string).ToLower().Contains("email"));

Assert.AreEqual(1, errorCalls.Count());

If this this is something you need frequently you could implement some helper methods and package this up into something fairly concise I think. (Maybe static void ReceivedCallToAny(this object substitute, string methodName, Func<object[], bool> requiredArgs) with some helpers like T GetItemAs<T>(object[] items) to access arguments?)

Upvotes: 7

Related Questions