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

所以我的代码中有一些SMTP内容,我正在尝试对该方法进行unit testing。

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

所以我想我必须亲自去做,这就是我被困住的地方。

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

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

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

MailMessage mail = new MailMessage(); mail.To.Add("test@hotmail.com"); 

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

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

 public MailAddressCollection To { get; } 

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

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

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

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

 void MailAddressCollection To(string email); or void string To.Add(string email); 

那我的包装怎么样?

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

编辑代码

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

 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; } 

谢谢

为什么要模拟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); } } 

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

这样,当您编写unit testing时,您将测试使用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")); } } 

编辑:为了解决您的评论,您需要两个单独的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); } } 

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

 class MockEmailService : IEmailService { private EmailMessage sentMessage;; public SentMailMessage { get { return sentMessage; } } public void SendEmail(MailMessage message) { sentMessage = message; } } 

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

你最终会在这里嘲笑几个不同的类(至少两个)。 首先,您需要一个围绕MailMessage类的包装器。 我将为包装器创建一个接口,然后让包装器实现接口。 在测试中,您将模拟界面。 其次,您将提供模拟实现作为对MailAddressCollection的模拟接口的期望。 由于MailAddressCollection实现了Collection ,因此这应该是相当简单的。 如果由于其他属性(我没有检查)模拟MailAddressCollection是有问题的,你可以让你的包装器将它作为IList ,作为一个接口应该很容易模拟。

 public interface IMailMessageWrapper { MailAddressCollection To { get; } } public class MailMessageWrapper { private MailMessage Message { get; set; } public MailMessageWrapper( MailMessage message ) { this.Message = message; } public MailAddressCollection To { get { return this.Message.To; } } } // RhinoMock syntax, sorry -- but I don't use Moq public void MessageToTest() { var message = MockRepository.GenerateMock() var to = MockRepository.GenerateMock(); var expectedAddress = "test@example.com"; message.Expect( m => m.To ).Return( to ).Repeat.Any(); to.Expect( t => t.Add( expectedAddress ) ); ... } 

免责声明:我在Typemock工作而不是找到一些黑客,你可以使用Typemock Isolator简单地在一行代码中伪造该类:

 var fakeMailMessage = Isolate.Fake.Instance(); 

然后,您可以使用Isolate.WhenCalled在其上设置行为

在.NET 4.0中,您可以使用“duck-typing”来传递另一个类而不是“System.Net.Mail”。 但是在早期版本中,我担心没有其他方法,而是在模拟类中创建“System.Net.Mail”的包装器。

如果是另一种(更好的)方式,我想学习它:)。

编辑:

 public interface IMailWrapper { /* members used from System.Net.Mail class */ } public class MailWrapper { private System.Net.Mail original; public MailWrapper( System.Net.Mail original ) { this.original = original; } /* members used from System.Net.Mail class delegated to "original" */ } public class MockMailWrapper { /* mocked members used from System.Net.Mail class */ } void YourMethodUsingMail( IMailWrapper mail ) { /* do something */ }