Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
540 views
in Technique[技术] by (71.8m points)

c# - Moq - What happens when using It.IsAny in a setup's return?

I am performing unit tests in C# using Moq. One test in particular I have created an interface wrapper over System.Net.Mail.SmtpClient so that it can be mocked.

public class SmtpClient : ISmtpClient
{
    public string Host { get; set; }
    public int Port { get; set; }
    public ICredentialsByHost Credentials { get; set; }
    public bool EnableSsl { get; set; }

    public void Send(MailMessage mail)
    {
        var smtpClient = new System.Net.Mail.SmtpClient
        {
            Host = Host,
            Port = Port,
            Credentials = Credentials,
            EnableSsl = EnableSsl
        };

        smtpClient.Send(mail);
    }
}

In my tests of this wrapper, to ensure that the method Send() is called, I have mocked the interface, and in setting up the mock, I'm using the Setup() to assign values to the properties of that object. In all documentation, I see that the .Return() of those setups are returning a specific value of the type that these methods are expecting. However, before I understood it further, I instead used It.IsAny<T> in the returns.

[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
    _smtpClientMock = new Mock<ISmtpClient>(MockBehavior.Strict);
    _smtpClientMock.Setup(x => x.Port).Returns(8080);
    _smtpClientMock.Setup(x => x.EnableSsl).Returns(false);
    _smtpClientMock.Setup(x => x.Host).Returns("host");
    _smtpClientMock.Setup(x => x.Credentials).Returns(It.IsAny<NetworkCredential>());

    _smtpClientMock.Setup(mockSend => mockSend.Send(It.IsAny<MailMessage>()));
}

[TestMethod]
public void WithValidMailMessageObject_WhenSendIsCalled_EmailClientCallsSmptClientToSendEmail()
{
    //Arrange

    //Act
    _smtpClientMock.Object.Send(new MailMessage());
    //Assert
    _smtpClientMock.Verify(checkMethodIsCalled => checkMethodIsCalled.Send(It.IsAny<MailMessage>()), Times.Once);
}

What I've noticed is that the tests passed. Since I haven't seen this elsewhere, I understand that this is not best practice. What I'm asking, is why is this not used, and what problems can come up with using It.IsAny<T>() inside the Return of a Moq's Setup() or a mocked object?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It is meant to be used in Moq expressions for the filtering and matching of arguments.

Allows the specification of a matching condition for an argument in a method invocation, rather than a specific argument value. "It" refers to the argument being matched.

It.IsAny<T>() is typically used when the actual argument value for a method call is not relevant. When passed as a value outside of the Setup or Verify expressions It.IsAny<T>() passes the default value of the generic argument. So for reference types it will pass null and so forth.

While in your scenario it does not fail, it is generally advised not to use the It class for anything other than matching arguments passed to mocked dependencies.

One typically uses the Returns to return a value of use when exercising a test. If a subject under test is expecting a value when a mock is invoked and instead the mock was Setup to return It.IsAny<T>(), then the test would behave in an unexpected manner.

Given the following simple example

public interface IDependency {
    string SomeMethod();
}

public MyClass {
    public bool MyMethod(IDependency input) {            
        var value = input.SomeMethod();

        var result = "Output" + value.ToUpper(); //<-- value should not be null

        return result != null;
    }
}

The following test will fail with a NullReferenceException because of the improper use of It.IsAny<T>()

[TestMethod]
public void MyMethod_Should_Return_True() {
    //Arrange
    var mock = new Mock<IDependency>();
    mock.Setup(_ => _.SomeMethod()).Returns(It.IsAny<string>());
    var subject = new MyClass();
    var expected = true;

    //Act
    var actual = subject.MyMethod(mock.Object);

    //Assert
    Assert.AreEqual(expected, actual);
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...