簡體   English   中英

使用NUnit的.NET的TDD工作流最佳實踐

[英]TDD workflow best-practices for .NET using NUnit

更新:我對此帖進行了重大更改 - 查看修訂歷史記錄以獲取詳細信息。

我開始使用NUnit深入研究TDD ,盡管我很喜歡在stackoverflow上查看我在這里找到的一些資源,但我經常發現自己沒有獲得良好的牽引力。

所以我真正想要實現的是獲得某種清單/工作流程 - 這就是我需要你們幫助我的地方 - 或者“ 測試計划 ”,它將為我提供合適的代碼覆蓋率。

因此,讓我們假設一個理想的情況,我們可以從頭開始一個項目,讓我們說一個Mailer助手類,它將具有以下代碼:

(我創建這個課程的目的只是為了通過代碼示例幫助解決問題,因此鼓勵任何批評或建議,並且非常歡迎)

Mailer.cs

using System.Net.Mail;
using System;

namespace Dotnet.Samples.NUnit
{
    public class Mailer
    {
        readonly string from;
        public string From { get { return from; } }

        readonly string to;
        public string To { get { return to; } }

        readonly string subject;
        public string Subject { get { return subject; } }

        readonly string cc;
        public string Cc { get { return cc; } }

        readonly string bcc;
        public string BCc { get { return bcc; } }

        readonly string body;
        public string Body { get { return body; } }

        readonly string smtpHost;
        public string SmtpHost { get { return smtpHost; } }

        readonly string attachment;
        public string Attachment { get { return Attachment; } }

        public Mailer(string from = null, string to = null, string body = null, string subject = null, string cc = null, string bcc = null, string smtpHost = "localhost", string attachment = null)
        {
            this.from = from;
            this.to = to;
            this.subject = subject;
            this.body = body;
            this.cc = cc;
            this.bcc = bcc;
            this.smtpHost = smtpHost;
            this.attachment = attachment;
        }

        public void SendMail()
        {
            if (string.IsNullOrEmpty(From))
                throw new ArgumentNullException("Sender e-mail address cannot be null or empty.", from);

            SmtpClient smtp = new SmtpClient();
            MailMessage mail = new MailMessage();
            smtp.Send(mail);
        }
    }
}

MailerTests.cs

using System;
using NUnit.Framework;
using FluentAssertions;

namespace Dotnet.Samples.NUnit
{
    [TestFixture]
    public class MailerTests
    {
        [Test, Ignore("No longer needed as the required code to pass has been already implemented.")]
        public void SendMail_FromArgumentIsNotNullOrEmpty_ReturnsTrue()
        {
            // Arrange
            dynamic argument = null;

            // Act
            Mailer mailer = new Mailer(from: argument);

            // Assert
            Assert.IsNotNullOrEmpty(mailer.From, "Parameter cannot be null or empty.");
        }

        [Test]
        public void SendMail_FromArgumentIsNullOrEmpty_ThrowsException()
        {
            // Arrange
            dynamic argument = null;
            Mailer mailer = new Mailer(from: argument);

            // Act
            Action act = () => mailer.SendMail();
            act.ShouldThrow<ArgumentNullException>();

            // Assert
            Assert.Throws<ArgumentNullException>(new TestDelegate(act));
        }

        [Test]
        public void SendMail_FromArgumentIsOfTypeString_ReturnsTrue()
        {
            // Arrange
            dynamic argument = String.Empty;

            // Act
            Mailer mailer = new Mailer(from: argument);

            // Assert
            mailer.From.Should().Be(argument, "Parameter should be of type string.");
        }

        // INFO: At this first 'iteration' I've almost covered the first argument of the method so logically this sample is nowhere near completed.
        // TODO: Create a test that will eventually require the implementation of a method to validate a well-formed email address.
        // TODO: Create as much tests as needed to give the remaining parameters good code coverage.
    }
}

因此,在我的前兩個失敗的測試之后,下一個明顯的步驟是實現使它們通過的功能,但是,我應該保留失敗的測試並在實現將使這些通過的代碼之后創建新的測試,或者我應該修改現有的讓它們通過之后?

關於這個主題的任何建議都會非常感激。

如果您安裝TestDriven.net ,其中一個組件(稱為NCover)實際上可以幫助您了解單元測試涵蓋了多少代碼。

除此之外,最好的解決方案是檢查每一行,並運行每個測試以確保您至少打過一次該行。

如果您使用像NUnit這樣的框架,可以使用AssertThrows這樣的方法,在這些方法中,您可以聲明方法在輸入時拋出所需的異常: httpAssertThrows

基本上,在給出良好和不良投入的情況下驗證預期行為是最佳起點。

我建議你選擇一些像NCover這樣的工具,它可以掛鈎你的測試用例來提供代碼覆蓋率統計。 如果您不需要許可版本,還有一個NCover社區版。

當人們(最終!)決定將測試覆蓋率應用於現有代碼庫時,測試所有內容是不切實際的; 你沒有資源,而且往往沒有太大的實際價值。

理想情況下,您要確保測試適用於新編寫/修改的代碼以及可能受這些更改影響的任何內容

要做到這一點,你需要知道:

  • 你改變了什么代碼。 您的源代碼管理系統將在此文件更改的級別為您提供幫助。

  • 執行新代碼后執行的代碼是什么。 為此,您需要一個可以跟蹤代碼的下游影響的靜態分析器(不知道其中的許多代碼)或測試覆蓋率工具,它可以顯示運行特定測試時執行的內容。 任何這樣執行的代碼也可能需要重新測試。

因為您希望最小化您編寫的測試代碼量,所以您顯然希望比“已更改”的文件精度粒度更好。 您可以使用diff工具(通常構建到源控件系統中)來幫助將焦點轉移到特定的行。 差異工具實際上並不理解代碼結構,因此他們報告的內容往往是面向行而不是面向結構,產生比必要更大的差異; 他們也沒有告訴你方便的測試訪問點,這可能是一種方法,因為整個單元測試的風格都集中在測試方法上。

你可以得到更好的差異工具。 我們的Smart Differencer工具提供了程序結構(表達式,語句,方法)和抽象編輯操作(插入,刪除,復制,移動,替換,重命名)方面的差異,這使得更容易解釋代碼更改。 這並沒有直接解決“哪種方法改變了?” 問題,但它通常意味着要做出很少的事情來做出決定。

您可以獲得將回答此問題的測試覆蓋率工具。 我們的測試覆蓋率工具可以將先前的測試覆蓋率運行與當前的測試覆蓋率運行進行比較,以告訴您必須重新運行哪些測試。 他們通過檢查代碼差異(類似於智能差異器)來做到這一點,但將更改抽象回方法級別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM