C#mock在实际代码中不起作用

时间:2015-03-26 12:39:25

标签: c# unit-testing mocking fakeiteasy moq-3

我目前使用以下代码编写代码:

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的属性,它会给我正确的存根属性:

MOCK SMTP CLIENT FROM TEST: Faked ServiCommTests.MockSmtpClient
SMTP HOST FROM UT: mydomain.com
SMTP PORT FROM UT: 22

正如您所看到的,在实际代码中它没有返回主机和默认端口,而在测试代码中它从模拟框架中提供了正确的存根属性,而对象在两种环境中都是相同的。

当我使用模拟框架Moq而不是FakeItEasy(如示例中)时,它完全相同。

提前致谢。

2 个答案:

答案 0 :(得分:1)

事实证明,当我将MockSmtpClient类更改为接口时,它可以正常工作。 Moq和Fakeiteasy显然不支持模拟正常的课程。

答案 1 :(得分:1)

正如@StevenScott所说,类可以被嘲笑,但有一些限制。您可以看到您描述的行为不是因为FakeItEasy失败,而是因为您实现和使用MockSmtpClient的方式。

HostPort属性会在SmtpClient中隐藏相同的属性。您可以在下面的警告中看到这一点:

enter image description here

因此,当您在编译器知道它是mockSmtpClient的情况下访问MockSmtpClient时,将调用新方法。在您的测试中,这意味着执行FakeItEasy提供的行为。

但是,生产代码只知道对象为SmtpClient,因此执行了类(非虚拟)方法。

为了演示,让我们完全不考虑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时的行为。