[英]How to write my first Unit-test for Email-function
这仅仅是一个电子邮件发送应用程序的小部分,但我试图实现对应用程序的单元测试。 到目前为止,我已经阅读了一些有关单元测试的内容,并且我了解您应该只测试单个函数而不是整个类。 如何测试此代码?
private void Send(IEnumerable<Customer> customers, string body)
{
string titleOfEmail = "Welcome as a new customer at Company!";
string ourEmailAddress = "info@company.com";
int NumberOfRetriesOnError = 2;
int DelayOnError = 10;
foreach (var customer in customers)
{
for (int i = 0; i <= NumberOfRetriesOnError; ++i)
{
try
{
Sender.Send(ourEmailAddress, customer.Email, titleOfEmail);
return;
}
catch (SmtpException e)
{
if (i < NumberOfRetriesOnError)
Thread.Sleep((i + 1) * DelayOnError);
else
Errors.Add(e.Message + customer); // Include customeremail
}
}
}
编辑:信息休息可能不是需要休息
public interface IMailSender
{
void Send(string from, string to, string body);
}
sealed class NullMailSender : IMailSender
{
void IMailSender.Send(string from, string to, string body)
{
}
}
sealed class SmtpMailSender : IMailSender
{
void IMailSender.Send(string from, string to, string body)
{
System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage();
mail.From = new System.Net.Mail.MailAddress(from);
System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient("yoursmtphost");
mail.To.Add(to);
mail.Body = body;
smtp.Send(mail);
}
}
这部分是顶部函数的其余部分,整个类
public class SendingMail
{
public List<string> Errors { get; } = new List<string>();
public IEnumerable<Customer> Customers { get; set; }
public IEnumerable<Order> Orders { get; set; }
public IMailSender Sender { get; set; }
public void SendWelcomeEmails()
{
var template = Resources.WelcomeEmailTemplate;
Send(GetNewCustomers(), Resources.WelcomeEmailTemplate);
}
public void SendComeBackEmail()
{
var template = Resources.WelcomeEmailTemplate;
var emailContent = String.Format(template);
Send(GetCustomersWithoutRecentOrders(), Resources.ComeBackEmailTemplate);
}
private IEnumerable<Customer> GetNewCustomers()
{
var yesterday = DateTime.Now.Date.AddDays(-1);
return Customers.Where(x => x.CreatedDateTime >= yesterday);
}
private IEnumerable<Customer> GetCustomersWithoutRecentOrders()
{
var oneMonthAgo = DateTime.Now.Date.AddMonths(-1);
return Customers.Where(c => {
var latestOrder = Orders
.Where(o => o.CustomerEmail == c.Email)
.OrderByDescending(o => o.OrderDatetime)
.FirstOrDefault();
return latestOrder != null
&& latestOrder.OrderDatetime < oneMonthAgo;
});
}
好的,我认为可能令您失望的是,您尚未按照SRP(单一责任原则)来行使职能。
这样说:您的职能目前负责什么?
...因此,立即开始,您的单元测试将不得不尝试处理函数调用中所有这些单独的事情。 单元测试讨厌这一点。
但是,如果您以不同的方式编写函数该怎么办?
// needs System.Net.Mail for its MailMessage class - but you could write your own
private void Send(IEnumerable<Customer> customers, string body)
{
foreach(Customer customer in customers)
Send(customer, body);
}
private void Send(Customer customer, string body)
{
MailMessage message = GenerateMessage(customer, body);
// your code for sending/retrying; omitted to keep the code short
}
private MailMessage GenerateMessage(Customer customer, string body)
{
string subject = "...";
string from = "...";
string to = customer.Email;
MailMessage retVal = new MailMessage(from, to, subject, body);
return retVal;
}
好的,现在看一下单元测试图片。 突然,您可以在不发送电子邮件的情况下测试电子邮件的生成。 只需创建一个虚拟客户,将其传递给GenerateMessage函数,然后验证它传递回的结果(甚至不会发送电子邮件)。
至于电子邮件本身? 那有点难。 您有两个选择。 选项1是只使用您的个人/群组的电子邮件地址生成一个虚拟客户,然后实际发送给您。 不理想,但是会确保电子邮件代码有效。 但是另一种选择是修改Sender类(或者,如果不控制它,则包装它)-类似于:
interface ISender
{
void Send(/* the args you've got for your send function */);
}
class Sender : ISender
{
void Send(/* args */) { /* code */ }
}
class DummySender : ISender
{
void Send(/* args */)
{
// code to validate the unit tests of Send() are passing in correctly (and you won't have it actually send anything.)
}
}
...然后,而不是直接调用“ Sender.Send”,而是传入您打算用于执行发送的ISender。 常规代码将在Sender实例中传递; 您的单元测试将通过DummySender的实例。
编辑(以帮助提供新信息)
IMailSender的那一部分? 这实际上是完美的。
首先,您可以将IMailSender对象作为附加参数传递给您的Send()函数,而不仅仅是将其挂接到“发件人”上。 然后,您可以编写如下内容:
public class UnitTestDummySender : IMailSender
{
public string fromAddressIShouldGet;
public string toAddressIShouldGet;
public string bodyIShouldGet;
public void Send(string from, string to, string body)
{
// check to make sure the arguments passed in match those properties
}
}
看看这是如何工作的? 您可以执行以下操作,而不仅仅是调用Send():
// your regular code:
Send(custList, body, myNormalSenderClass);
// your unit test code:
UnitTestDummySender faker = new UnitTestDummySender();
// lines to set the faker properties to be what the email should work out to be
Send(myFakeCustList, body, faker);
希望有帮助! :-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.