多亏了 Mock,如何避免 xunit.net 中的基调用方法

How to avoid base called method in xunit.net thanks to Mock

我正在尝试为我的 CutomSmtpMailer 编写单元测试代码。我的 CutomSmtpMailer 继承自 SmtpClient。

这是我的 class 的样子:

public class CutomSmtpMailer : SmtpClient
{
    private void SendMail(MailMessage mailMessage)
    {
        //DoSomeStuff
        Send(mailMessage); //base.Send(mailMessage)
    }
}

我想在不发送邮件的情况下测试我的自定义代码:我想通过用空操作替换它来避免调用 "Send(mailMessage)",不知道它是否已被调用。当我使用 DI 时,我知道如何模拟一个实例,但我不想注入 SMTP 客户端(事实上我有很多继承的案例,这里是一个简单的例子)

public class TestCutomSmtpMailer
{
    public CutomSmtpMailer Get()
    {
        return new CutomSmtpMailer();
    }

    [Fact]
    public void SendMail()
    {
        CutomSmtpMailer service = Get();
        MailMessage mailMessage = new MailMessage();
        // Find on web but not available :(
        Mock.Arrange(() => new SmtpClient().Send()).Returns(null).IgnoreInstance();
        service.SendMail(mailMessage);
    }
}

如何通过空函数 replace/Mock Parent class 方法来避免发送邮件?

提前致谢

因为您不想使用 依赖注入 来注入 SmtpClient,您可以采用丑陋的方式添加一个 内部 构造函数并委托给 CustomSmtpMailer 以帮助模拟和测试。 请注意,这种方法不是很干净。 这将导致在您的产品装配中产生纯粹用于测试的代码,这通常不是一个好主意。

然而,它确实解决了您所描述的问题,让您不必使用 依赖注入

public class CustomSmtpMailer : SmtpClient {

    internal delegate void SendInternal(MailMessage message);

    private SendInternal _sendAction;

    // make sure all your constructors call this one.
    // this will make the call to base.Send(MailMessage) the default behaviour.
    public CustomSmtpMailer() {
        _sendAction = delegate (MailMessage message) { Send(message); };
    }

    // customizes the SendMail(MailMessage) behaviour for testing purposes.
    internal CustomSmtpMailer(SendInternal sendAction) {
        _sendAction = sendAction;
    }

    private void SendMail(MailMessage mailMessage) {
        //DoSomeStuff

        _sendAction(mailMessage);
    }

}

通过将其添加到项目中的 AssemblyInfo.cs,使内部成员对您的测试项目可见。

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("My.Test.Assembly.Name")]

在您的单元测试中,您可以使用内部构造函数来配置碱基调用。

[Fact]
public void SendMail() {

    CustomSmtpMailer service = new CustomSmtpMailer(delegate (MailMessage message) {
        Console.WriteLine("I'm a custom Action that can be used for testing");
    });
    MailMessage mailMessage = new MailMessage();
    // Find on web but not available :(
    Mock.Arrange(() => new SmtpClient().Send()).Returns(null).IgnoreInstance();
    service.SendMail(mailMessage);

}