C# 模拟在实际代码中不起作用
C# mock not working in actual code
我目前有以下代码方法:
public bool SendMail(ref MailData data)
SmtpClient smtpClient = this.smtpClient;
Console.WriteLine("SMTP CLIENT: " + smtpClient.ToString());
Console.WriteLine("SMTP PORT: " + smtpClient.Port);
Console.WriteLine("SMTP HOST: " + smtpClient.Host);
}
使用以下测试代码调用它:
public class MockSmtpClient : SmtpClient {
public virtual void Send(MailMessage mailMessage) {
}
public virtual string Host
{
get
{
return Host;
}
}
public virtual int Port
{
get
{
return Port;
}
}
}
... (actual testcode:) ...
MailData mailData = new MailData {
HostAddress = "mydomain.com",
};
var mockSmtpClient = A.Fake<MockSmtpClient>();
var mailerMock = new Mailer();
// Setup
It.IsAny<SmtpDeliveryMethod>())).Returns(mockSmtpClient.Object);
A.CallTo(() => mockSmtpClient.Host).Returns(mailData.HostAddress);
A.CallTo(() => mockSmtpClient.Port).Returns(22);
// Act
mailerMock.smtpClient = mockSmtpClient;
mailerMock.SendMail(ref mailData);
Console.WriteLine("MOCK SMTP CLIENT FROM TEST: " + mockSmtpClient);
Console.WriteLine("SMTP HOST FROM UT: " + mockSmtpClient.Host);
Console.WriteLine("SMTP PORT FROM UT: " + mockSmtpClient.Port);
当我在实际代码中调用模拟对象的主机和端口时,它会给我 \
SMTP CLIENT: Faked ServiCommTests.MockSmtpClient
SMTP PORT: 25
SMTP HOST:
但是,如果我从测试代码中调用模拟的属性,它会为我提供正确的存根属性:
MOCK SMTP CLIENT FROM TEST: Faked ServiCommTests.MockSmtpClient
SMTP HOST FROM UT: mydomain.com
SMTP PORT FROM UT: 22
如您所见,在实际代码中,它没有返回主机和默认端口,而在测试代码中,它为我提供了来自模拟框架的正确存根属性,而对象在两种环境中都是相同的。
当我使用模拟框架 Moq 而不是 FakeItEasy(如示例中)时,情况完全相同。
提前致谢。
事实证明,当我将 Mock SmtpClient class 更改为接口时它起作用了。 Moq 和 Fakeiteasy 显然不支持模拟正常 classes.
正如@StevenScott 所说,classes 可以被模拟,但有一些限制。您看到您描述的行为不是因为 FakeItEasy 失败,而是因为您实现和使用 MockSmtpClient
的方式。
Host
和 Port
属性隐藏了 SmtpClient
中的相同属性。您可以在下面的警告中看到这一点:
因此,当您在编译器 知道 它是 MockSmtpClient
的情况下访问 mockSmtpClient
时,将调用新方法。在您的测试中,这意味着执行了 FakeItEasy 提供的行为。
但是,生产代码只知道该对象是 SmtpClient
,因此执行 class 的(非虚拟)方法。
为了演示,让我们完全去掉 FakeItEasy,对 MockSmtpClient
进行简单更改:
public class MockSmtpClient : SmtpClient
{
public virtual void Send(MailMessage mailMessage)
{
}
public virtual string Host
{
get { return "mocksmtpclient.local"; }
}
public virtual int Port
{
get { return 17; }
}
}
和这个测试:
[Test]
public void Test2()
{
MailData mailData = new MailData
{
HostAddress = "mydomain.com",
};
var mockSmtpClient = new MockSmtpClient();
var mailerMock = new Mailer();
// Act
mailerMock.smtpClient = mockSmtpClient;
mailerMock.SendMail(ref mailData);
Console.WriteLine("SMTP CLIENT FROM TEST: " + mockSmtpClient);
Console.WriteLine("SMTP HOST FROM TEST: " + mockSmtpClient.Host);
Console.WriteLine("SMTP PORT FROM TEST: " + mockSmtpClient.Port);
}
这会产生以下输出:
SMTP CLIENT: GighaQuestion.MockSmtpClient
SMTP HOST:
SMTP PORT: 25
SMTP CLIENT FROM TEST: GighaQuestion.MockSmtpClient
SMTP HOST FROM TEST: mocksmtpclient.local
SMTP PORT FROM TEST: 17
这类似于您使用 FakeItEasy 创建 mockSmtpClient
时的行为。
我目前有以下代码方法:
public bool SendMail(ref MailData data)
SmtpClient smtpClient = this.smtpClient;
Console.WriteLine("SMTP CLIENT: " + smtpClient.ToString());
Console.WriteLine("SMTP PORT: " + smtpClient.Port);
Console.WriteLine("SMTP HOST: " + smtpClient.Host);
}
使用以下测试代码调用它:
public class MockSmtpClient : SmtpClient {
public virtual void Send(MailMessage mailMessage) {
}
public virtual string Host
{
get
{
return Host;
}
}
public virtual int Port
{
get
{
return Port;
}
}
}
... (actual testcode:) ...
MailData mailData = new MailData {
HostAddress = "mydomain.com",
};
var mockSmtpClient = A.Fake<MockSmtpClient>();
var mailerMock = new Mailer();
// Setup
It.IsAny<SmtpDeliveryMethod>())).Returns(mockSmtpClient.Object);
A.CallTo(() => mockSmtpClient.Host).Returns(mailData.HostAddress);
A.CallTo(() => mockSmtpClient.Port).Returns(22);
// Act
mailerMock.smtpClient = mockSmtpClient;
mailerMock.SendMail(ref mailData);
Console.WriteLine("MOCK SMTP CLIENT FROM TEST: " + mockSmtpClient);
Console.WriteLine("SMTP HOST FROM UT: " + mockSmtpClient.Host);
Console.WriteLine("SMTP PORT FROM UT: " + mockSmtpClient.Port);
当我在实际代码中调用模拟对象的主机和端口时,它会给我 \
SMTP CLIENT: Faked ServiCommTests.MockSmtpClient
SMTP PORT: 25
SMTP HOST:
但是,如果我从测试代码中调用模拟的属性,它会为我提供正确的存根属性:
MOCK SMTP CLIENT FROM TEST: Faked ServiCommTests.MockSmtpClient
SMTP HOST FROM UT: mydomain.com
SMTP PORT FROM UT: 22
如您所见,在实际代码中,它没有返回主机和默认端口,而在测试代码中,它为我提供了来自模拟框架的正确存根属性,而对象在两种环境中都是相同的。
当我使用模拟框架 Moq 而不是 FakeItEasy(如示例中)时,情况完全相同。
提前致谢。
事实证明,当我将 Mock SmtpClient class 更改为接口时它起作用了。 Moq 和 Fakeiteasy 显然不支持模拟正常 classes.
正如@StevenScott 所说,classes 可以被模拟,但有一些限制。您看到您描述的行为不是因为 FakeItEasy 失败,而是因为您实现和使用 MockSmtpClient
的方式。
Host
和 Port
属性隐藏了 SmtpClient
中的相同属性。您可以在下面的警告中看到这一点:
因此,当您在编译器 知道 它是 MockSmtpClient
的情况下访问 mockSmtpClient
时,将调用新方法。在您的测试中,这意味着执行了 FakeItEasy 提供的行为。
但是,生产代码只知道该对象是 SmtpClient
,因此执行 class 的(非虚拟)方法。
为了演示,让我们完全去掉 FakeItEasy,对 MockSmtpClient
进行简单更改:
public class MockSmtpClient : SmtpClient
{
public virtual void Send(MailMessage mailMessage)
{
}
public virtual string Host
{
get { return "mocksmtpclient.local"; }
}
public virtual int Port
{
get { return 17; }
}
}
和这个测试:
[Test]
public void Test2()
{
MailData mailData = new MailData
{
HostAddress = "mydomain.com",
};
var mockSmtpClient = new MockSmtpClient();
var mailerMock = new Mailer();
// Act
mailerMock.smtpClient = mockSmtpClient;
mailerMock.SendMail(ref mailData);
Console.WriteLine("SMTP CLIENT FROM TEST: " + mockSmtpClient);
Console.WriteLine("SMTP HOST FROM TEST: " + mockSmtpClient.Host);
Console.WriteLine("SMTP PORT FROM TEST: " + mockSmtpClient.Port);
}
这会产生以下输出:
SMTP CLIENT: GighaQuestion.MockSmtpClient
SMTP HOST:
SMTP PORT: 25
SMTP CLIENT FROM TEST: GighaQuestion.MockSmtpClient
SMTP HOST FROM TEST: mocksmtpclient.local
SMTP PORT FROM TEST: 17
这类似于您使用 FakeItEasy 创建 mockSmtpClient
时的行为。