问题
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?
回答1:
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);
}
来源:https://stackoverflow.com/questions/49761943/moq-what-happens-when-using-it-isany-in-a-setups-return