如何制作System.Net.Mail MailMessage的模型?

cho*_*bo2 19 c# asp.net-mvc unit-testing mocking

所以我的代码中有一些SMTP内容,我正在尝试对该方法进行单元测试.

所以我一直在尝试使用Mockup MailMessage但它似乎永远不会起作用.我认为没有一种方法是虚拟的或抽象的,所以我不能用moq来模拟它:(.

所以我想我必须手工完成,这就是我被困住的地方.

*手动我的意思是使用界面和包装器,但让moq仍然模拟界面.

我不知道如何编写我的接口和我的Wrapper(一个将实现具有实际MailMessage代码的接口的类,因此当我的真实代码运行时它实际上完成了它需要做的事情).

所以首先我不确定如何设置我的界面.让我们来看看我必须模拟的其中一个字段.

MailMessage mail = new MailMessage();

mail.To.Add("test@hotmail.com");
Run Code Online (Sandbox Code Playgroud)

所以这是我要假装的第一件事.

所以看着它我知道"To"是一个属性,通过命中F12而不是"To"它需要我这一行:

public MailAddressCollection To { get; }
Run Code Online (Sandbox Code Playgroud)

所以它是MailAddressCollection属性.但有些我怎么被允许进一步做"添加".

所以现在我的问题是在我的界面中我做了什么?

我要做房产吗?该属性应该是MailAddressCollection吗?

或者我应该有一个像这样的方法?

void MailAddressCollection To(string email);

or 

void string To.Add(string email);
Run Code Online (Sandbox Code Playgroud)

那我的包装怎么样?

所以你可以看到我很困惑.因为有这么多.我猜我只是模拟我正在使用的那些.

编辑代码

我想在真正意义上我只需要测试更多异常,但我想测试以确保一切都被发送然后它将得到响应=成功.

string response = null;
            try
            {

                MembershipUser userName = Membership.GetUser(user);

                string newPassword = userName.ResetPassword(securityAnswer);

                MailMessage mail = new MailMessage();

                mail.To.Add(userName.Email);

                mail.From = new MailAddress(ConfigurationManager.AppSettings["FROMEMAIL"]);
                mail.Subject = "Password Reset";

                string body = userName + " Your Password has been reset. Your new temporary password is: " + newPassword;

                mail.Body = body;
                mail.IsBodyHtml = false;


                SmtpClient smtp = new SmtpClient();

                smtp.Host = ConfigurationManager.AppSettings["SMTP"];
                smtp.Credentials = new System.Net.NetworkCredential(ConfigurationManager.AppSettings["FROMEMAIL"], ConfigurationManager.AppSettings["FROMPWD"]);

                smtp.EnableSsl = true;

                smtp.Port = Convert.ToInt32(ConfigurationManager.AppSettings["FROMPORT"]);

                smtp.Send(mail);

                response = "Success";
            }
            catch (ArgumentNullException ex)
            {
                response = ex.Message;

            }
            catch (ArgumentException ex)
            {
                response = ex.Message;

            }
            catch (ConfigurationErrorsException ex)
            {
                response = ex.Message;
            }
            catch (ObjectDisposedException ex)
            {
                response = ex.Message;
            }
            catch (InvalidOperationException ex)
            {
                response = ex.Message;
            }
            catch (SmtpFailedRecipientException ex)
            {
                response = ex.Message;
            }
            catch (SmtpException ex)
            {
                response = ex.Message;
            }



            return response;

        } 
Run Code Online (Sandbox Code Playgroud)

谢谢

Jer*_*rey 32

为什么要模拟MailMessage?SmtpClient接收MailMessages并将它们发送出去; 这是我想要用于测试目的的类.因此,如果您正在编写某种类型的放置订单的系统,如果您尝试测试订单放置后您的OrderService始终发送电子邮件,那么您将拥有类似于以下内容的类:

class OrderService : IOrderSerivce 
{
    private IEmailService _mailer;
    public OrderService(IEmailService mailSvc) 
    {
        this. _mailer = mailSvc;
    }

    public void SubmitOrder(Order order) 
    {
        // other order-related code here

        System.Net.Mail.MailMessage confirmationEmail = ... // create the confirmation email
        _mailer.SendEmail(confirmationEmail);
    } 

}
Run Code Online (Sandbox Code Playgroud)

使用IEtService的默认实现包装SmtpClient:

这样,当您编写单元测试时,您将测试使用SmtpClient/EmailMessage类的代码的行为,而不是SmtpClient/EmailMessage类本身的行为:

public Class When_an_order_is_placed
{
    [Setup]
    public void TestSetup() {
        Order o = CreateTestOrder();
        mockedEmailService = CreateTestEmailService(); // this is what you want to mock
        IOrderService orderService = CreateTestOrderService(mockedEmailService);
        orderService.SubmitOrder(o);
    } 

    [Test]
    public void A_confirmation_email_should_be_sent() {
        Assert.IsTrue(mockedEmailService.SentMailMessage != null);
    }


    [Test]
    public void The_email_should_go_to_the_customer() {
        Assert.IsTrue(mockedEmailService.SentMailMessage.To.Contains("test@hotmail.com"));
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑:为了解决您的评论,您需要两个单独的EmailService实现 - 只有一个将使用SmtpClient,您将在应用程序代码中使用它:

class EmailService : IEmailService {
    private SmtpClient client;

    public EmailService() {
        client = new SmtpClient();
        object settings = ConfigurationManager.AppSettings["SMTP"];
        // assign settings to SmtpClient, and set any other behavior you 
        // from SmtpClient in your application, such as ssl, host, credentials, 
        // delivery method, etc
    }

    public void SendEmail(MailMessage message) {
        client.Send(message);
    }

}
Run Code Online (Sandbox Code Playgroud)

你的模拟/伪造电子邮件服务(你不需要一个模拟框架,但它有帮助)不会触及SmtpClient或SmtpSettings; 它只记录了一个事实,即在某个时候,一封电子邮件通过SendEmail传递给它.然后,您可以使用它来测试是否调用了SendEmail,以及使用哪些参数:

class MockEmailService : IEmailService {
    private EmailMessage sentMessage;;

    public SentMailMessage { get { return sentMessage; } }

    public void SendEmail(MailMessage message) {
        sentMessage = message;
    }

}
Run Code Online (Sandbox Code Playgroud)

电子邮件是否已发送到SMTP服务器并交付的实际测试应超出单元测试的范围.您需要知道这是否有效,并且您可以设置第二组测试来专门测试它(通常称为集成测试),但这些是与测试应用程序核心行为的代码分开的不同测试.

  • 对.您不想测试MailMessage或SmtpClient; 这不是你的代码.您希望对使用MailMessage和SmtpClient的所有内容进行单元测试,以确保:a)他们正在使用正确的字段创建MailMessage; 和b)实际上是发送电子邮件. (3认同)