2017-08-15 3 views
-1

これは電子メール送信アプリケーションのほんの一部ですが、アプリケーションの単体テストを実装しようとしています。これまでの単体テストで少し読んだことがありますが、クラス全体ではなく単一の関数をテストするだけでよいことを理解しています。このコードをテストするにはどうすればよいですか?私の最初のUnit-test for Email-functionを書く方法

private void Send(IEnumerable<Customer> customers, string body) 
     { 
      string titleOfEmail = "Welcome as a new customer at Company!"; 
      string ourEmailAddress = "[email protected]"; 
      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; 
     }); 
    } 

答えて

1

大丈夫です、私はSRP(Single Responsibility Principle)に基づいて自分の機能を壊していないことが、あなたを引きつけるかもしれないと考えてください。

このようにする:あなたの機能は現在何を担当していますか?住所、それぞれの顧客

  • 呼び出す送信者をループメール
  • エラー処理
  • ...ので、右のバットタイトル&を設定

    1. 、あなた単体テストは、その関数の呼び出しで、それらの別々のものすべてを処理しようとする必要があります。ユニットテストは嫌いです。

      しかし、あなたの機能を別に書いた場合はどうなりますか?

      // 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関数に渡し、返された結果を検証します(メールは送信されません)。

      電子メール自体については、それは少し厳しいです。あなたには2つの選択肢があります。オプション1は、個人/グループの電子メールアドレスを持つダミーの顧客を生成し、実際に先に進んでメールを送信することです。理想的ではありませんが、電子メールコードが正しく動作することを確認します。あなたは、代わりに「Sender.Send」を直接に呼び出すので、その後、

      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.) 
          } 
      } 
      

      ...と:何か - しかし、別のオプションは、(あなたがそれをコントロールしていない場合は、それをまたはラップ)送信者のクラスを変更することです送信を実行する予定のISenderを渡します。通常のコードはSenderのインスタンスで渡されます。あなたのユニットテストはDummySenderのインスタンスで渡されます。

      EDIT(与えられた新しい情報を助けるために)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); 
      

      :-)

    +0

    私は少し新人ですが、勉強しようとすると、最初の部分を完全に理解しました。しかし、私たちが電子メールの部分に着いたとき、私は他のクラスのファイルによく似ていたので、混乱しました。あなたが私のことを理解する必要があるかどうかは分かりませんが、インターフェースと実装した残りのクラスの質問を編集しました。 –

    関連する問題