簡體   English   中英

單元測試:自包含測試與代碼重復(DRY)

[英]Unit Testing: Self-contained tests vs code duplication (DRY)

我正在進行單元測試的第一步,並且不確定兩個在單元測試中似乎自相矛盾的范例,即:

  • 每個單元測試都應該是獨立的,而不是依賴於其他測試。
  • 不要重復自己。

更具體一點,我有一個我想測試的進口商。 導入器具有“導入”功能,可以獲取原始數據(例如,從CSV中刪除)並返回某種類型的對象,該對象也將通過ORM(在本例中為LinqToSQL)存儲到數據庫中。

現在我想測試幾件事情,例如返回的返回對象不為null,它的必填字段不為null或為空,並且它的屬性得到了正確的值。 我為此寫了3個單元測試。 每個測試應該導入並獲得作業還是屬於一般的設置邏輯? 另一方面, 相信這篇博文 ,就我的理解而言,后者將是一個壞主意。 此外,這不會違反自我遏制嗎?

我的班級看起來像這樣:

[TestFixture]
public class ImportJob
{
    private TransactionScope scope;
    private CsvImporter csvImporter;

    private readonly string[] row = { "" };

    public ImportJob()
    {
        CsvReader reader = new CsvReader(new StreamReader(
                    @"C:\SomePath\unit_test.csv", Encoding.Default),
                    false, ';');
        reader.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;

        int fieldCount = reader.FieldCount;
        row = new string[fieldCount];

        reader.ReadNextRecord();
        reader.CopyCurrentRecordTo(row);
    }

    [SetUp]
    public void SetUp()
    {
        scope = new TransactionScope();
        csvImporter = new CsvImporter();
    }

    [TearDown]
    public void TearDown()
    {
        scope.Dispose();
    }

    [Test]
    public void ImportJob_IsNotNull()
    {
        Job j = csvImporter.ImportJob(row);

        Assert.IsNotNull(j);
    }

    [Test]
    public void ImportJob_MandatoryFields_AreNotNull()
    {
        Job j = csvImporter.ImportJob(row);

        Assert.IsNotNull(j.Customer);
        Assert.IsNotNull(j.DateCreated);
        Assert.IsNotNull(j.OrderNo);
    }

    [Test]
    public void ImportJob_MandatoryFields_AreValid()
    {
        Job j = csvImporter.ImportJob(row);
        Customer c = csvImporter.GetCustomer("01-01234567");

        Assert.AreEqual(j.Customer, c);
        Assert.That(j.DateCreated.Date == DateTime.Now.Date);
        Assert.That(j.OrderNo == row[(int)Csv.RechNmrPruef]);

    }

    // etc. ...
}

可以看出,我正在做一行Job j = csvImporter.ImportJob(row); 在每個單元測試中,因為它們應該是獨立的。 但這確實違反了DRY原則,並可能在某一天導致性能問題。

在這種情況下,最佳做法是什么?

您的測試類與通常的類沒有什么不同,應該這樣對待:所有良好實踐(DRY,代碼重用等)也應該適用於那里。

這取決於您的測試中常見的場景數量。 在博客文章中你提到主要的抱怨是SetUp方法為三個測試做了不同的設置,這不能被認為是最佳實踐。 在您的情況下,您為每個測試/場景設置了相同的設置,然后您應該使用共享的SetUp而不是在每個測試中復制代碼。 如果您稍后發現有更多測試不共享此設置或需要在一組測試之間共享不同的設置,則將這些測試重構為新的測試用例類。 您也可以使用未使用[SetUp]標記的共享設置方法,但在每個需要它們的測試開始時調用它們:

[Test]
public void SomeTest()
{
   setupSomeSharedState();
   ...
}

找到正確混合的一種方法可能是在沒有SetUp方法的情況下開始,當你發現你正在為測試設置復制代碼然后重構一個共享方法。

你可以把Job j = csvImporter.ImportJob(row); 在你的設置中。 這樣你就不會重復代碼了。

實際上你應該為每個測試運行那行代碼。 否則,由於其他測試中發生的事情,測試將開始失敗。 這將變得難以維持。

性能問題不是由DRY違規引起的。 實際上,您應該為每個測試設置所有內容。 這些不是單元測試,它們是集成測試,您依靠外部文件來運行測試。 您可以從流中讀取ImportJob,而不是直接打開文件。 然后你可以測試一個內存流。

你是否搬家

Job j = csvImporter.ImportJob(row);

無論是否進入SetUp函數,它仍將在每次執行測試之前執行。 如果在每個測試的頂部都有完全相同的行,那么將該行移動到SetUp部分是合乎邏輯的。

您發布的博客條目抱怨測試值的設置是在與測試本身斷開連接的功能(可能不在同一屏幕上)中完成的 - 但是您的情況不同,因為測試數據是由外部文本文件,以便投訴與您的特定用例不匹配。

在我的一個項目中,我們同意團隊的意見, 我們不會在單元測試構造函數中實現任何初始化邏輯 我們有Setup,TestFixtureSetup,SetupFixture(自NUnit版本2.4起)屬性。 當我們需要初始化時,幾乎所有情況都足夠了。 我們強制開發人員使用其中一個屬性並明確定義我們是否會在每次測試之前,在fixture中的所有測試之前或命名空間中的所有測試之前運行此初始化代碼。

但是,我不同意單元測試應該始終確認所有適用於通常開發的良好實踐。 這是可取的,但這不是一個規則。 我的觀點是,在現實生活中,客戶不支付單元測試費用 客戶支付產品的整體質量和功能。 他不想知道您是否通過單元測試/自動化GUI測試覆蓋100%的代碼或者每個開發人員使用3個手動測試器來為他提供無錯誤的產品,每個開發人員會在每次構建后點擊每個屏幕。 單元測試不會為產品增加業務價值,它們允許您節省開發和測試工作,並迫使開發人員編寫更好的代碼。 所以它總是取決於你 - 你會花多少時間進行UT重構以使單元測試變得完美嗎? 或者您是否會花費相同的時間為產品的客戶添加新功能? 不要忘記單元測試應該盡可能簡單。 如何找到黃金分割?

我認為這取決於項目,PM或團隊負責人需要計划和估計單元測試的質量,完整性和代碼覆蓋率,就好像他們估計了產品的所有其他業務功能一樣。 我認為,最好是復制粘貼單元測試覆蓋80%的生產代碼,然后進行設計精良且分離的單元測試,僅覆蓋20%。

暫無
暫無

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

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