[英]How do I make a mockup of System.Net.Mail MailMessage?
所以我的代码中有一些 SMTP 内容,我正在尝试对该方法进行单元测试。
所以我一直在尝试 Mockup MailMessage,但它似乎从未奏效。 我认为这些方法都不是虚拟的或抽象的,所以我不能使用 moq 来模拟它:(。
所以我想我必须手工完成,这就是我被卡住的地方。
*手动我的意思是知道接口和包装器,但让 moq 仍然模拟接口。
我不知道如何编写我的接口和我的包装器(一个将实现接口的类,该接口将具有实际的 MailMessage 代码,因此当我的实际代码运行时,它实际上会完成它需要做的事情)。
所以首先我不确定如何设置我的界面。 让我们来看看我必须模拟的领域之一。
MailMessage mail = new MailMessage();
mail.To.Add("test@hotmail.com");
所以这是我必须伪造的第一件事。
所以看着它,我知道“To”是一个属性,通过在“To”上按 F12 它将我带到这一行:
public MailAddressCollection To { get; }
所以它是 MailAddressCollection 属性。 但是有些我被允许走得更远并做“添加”。
所以现在我的问题是在我的界面中我要做什么?
我做一个财产? 这个属性应该是 MailAddressCollection 吗?
或者我应该有一个方法?
void MailAddressCollection To(string email);
or
void string To.Add(string email);
那么我的包装纸会是什么样子呢?
正如你所看到的,我很困惑。 因为有这么多。 我猜我只是模拟了我正在使用的那些。
编辑代码
我想在真正的意义上,我只需要测试更多的异常,但我想测试以确保是否发送了所有内容,然后它将获得响应 = 成功。
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 接收邮件消息并发送出去; 那是我想为测试目的包装的类。 因此,如果您正在编写某种类型的下订单系统,如果您尝试测试下订单时 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);
}
}
使用 IEmailService 包装 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"));
}
}
编辑:为了解决您在下面的评论,您需要两个单独的 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 服务器并被传送的实际测试应该超出单元测试的范围。 您需要知道这是否有效,并且您可以设置第二组测试来专门对此进行测试(通常称为集成测试),但这些是独立于测试应用程序核心行为的代码的不同测试。
您最终将在这里模拟几个不同的类(至少两个)。 首先,您需要一个围绕 MailMessage 类的包装器。 我将为包装器创建一个接口,然后让包装器实现该接口。 在您的测试中,您将模拟界面。 其次,您将提供一个模拟实现,作为对 MailAddressCollection 的模拟接口的期望。 由于 MailAddressCollection 实现了Collection<MailAddress>
,这应该是相当简单的。 如果由于附加属性(我没有检查)而模拟 MailAddressCollection 有问题,您可以让包装器将其作为IList<MailAddress>
返回,作为接口应该很容易模拟。
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<IMailMessageWrapper>()
var to = MockRepository.GenerateMock<MailAddressCollection>();
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<MailMessage>();
然后你可以使用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 */
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.