簡體   English   中英

為何以及如何在遺留應用程序代碼中實現初始單元測試

[英]Why and how implementing initial unit tests in legacy application code

我正在將單元測試集成到現有的遺留應用程序中。 在“使用遺留應用程序”一書和我閱讀的許多其他書籍中,有人寫道,在開始重構現有代碼或集成新功能,糾正錯誤等過程之前,您應始終編寫單元測試...

在我閱讀的大量樣本中,重構方法的簽名從未或很少中斷,舊的單元測試在經過大量更改后仍然有效。 原因是作者代碼不是那么遺留,以至於當我使用我認為的“遺留代碼”時,我每天查看的代碼。

實際上,當你有一個遺留應用程序時,代碼是如此糟糕,你必須打破方法的簽名。 如果您嘗試使用原始方法編寫單元測試,只需更改5分鍾后,您將打破整個簽名,第一次測試將很好地發送到垃圾箱。

舉個例子,看看下面的代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyCompany.Accouting
{
    public class DataCreator
    {
        public static System.Data.DataSet CreateInvoice(
            System.Data.DataSet customer, 
            System.Data.DataSet order, 
            string mails, 
            ref bool isValid)
        {
            System.Data.DataSet invoice = new System.Data.DataSet();

            int taxGroupId =
                ApplicationException.ShareConnection.ExecuteScalar(
                    "SELECT Id FROM TaxGroup WHERE TaxGroup.IsDefault");

            Application.ShareConnection.ExecuteNonQuery(
                "INSERT INTO Invoice (CustomerId, EffectiveDate) VALUES(?,?)",
                 customer.Tables[0].Rows[0]["Id"], System.DateTime.Now);

            int invoiceId;

            invoiceId = Application.SharedConnection.ExecuteScalar("SELECT @@IDENTITY");

            Application.SharedConnection.ExecuteNonQuery(
                "INSERT INTO InvoiceLine (ProductId, Quantity, InvoiceId) VALUES(?,?,?)", ,
                order.Tables[0].Rows[0]["ProductId"], order.Tables[0].Rows[0]["Quantity"], invoiceId);

            foreach(string mail in mails.Split(';'))
            {
                Application.MailSender.Send(mail);
            }

            isValid = true;

            System.Data.DataRow row = invoice.Tables[0].NewRow();
            row["Id"] = invoiceId;

            invoice.Tables[0].Rows.Add(row);

            return invoice;
        }
    }
}

正如您所看到的,這里有很多不良代碼。

重構后,方法不會是靜態的,ref參數將被刪除,DataSet將被轉換為POCO對象,對“Application”這樣的全局對象的訪問將被動態注入的屬性所取代,並且將進行許多其他更改實現接口,查看類的名稱,命名空間和許多其他東西。 事實上,這段代碼完全是一個廢話,應該扔掉並從頭開始重寫。

如果我為原始靜態方法創建單元測試,則在刪除static關鍵字以便以更面向對象的方式使用該類時,測試將立即中斷。 將DataSet更改為Poco等也是如此...

為什么在5分鍾內創建一個單元測試,我會丟掉這個測試? 這項測試有用嗎?

在這種情況下你會使用哪種策略?

非常感謝你。

這里的關鍵項目是選擇您實際要進行單元測試的點。 在您的情況下,對您要替換的確切方法進行測試沒有意義。 相反,需要為應用程序中調用方法的每個點創建一個測試,以確保特定功能仍然相同。

原因是,一旦完成了對DataCreator類的重構,您將不得不回到調用它的所有區域並更改它們。 在進行更改之前對這些區域進行測試將確保您的功能相同。

見下文:

public class SomeClass {
  public Boolean DoSomething() {
    OtherClass oc = new OtherClass();
    return oc.DoSomethingElse("param1", "param2") == "true";
  }
}


public class OtherClass {
  public String DoSomethingElse(String param1, String param2) {
      // horrible code here which never uses the second parameter

     return "true";
  }
}

在上面的示例中,您可能非常希望重構DoSomethingElse以將返回類型更改為布爾值並消除第二個參數。

所以你首先在SomeClass.DoSomething方法上進行單元測試。 然后將OtherClass重構為您的內容,確保DoSomething的最終結果是相同的。

當然,在這種情況下,您需要確保對每個調用“DoSomethingElse”的東西進行單元測試

您的單元測試將始終隨着簽名更改而改變。 解決此問題的最佳方法是設置測試一般行為的單元測試,並首先進行簡單的優化。

例如,從優化函數代碼本身開始(例如,修復數據訪問並將函數拆分為一對。)

然后你可以進入簽名重構,但是在你做之前,確保使用這個類的組件有基本的期望結果測試,這樣你就知道在刪除out參數的過程中,你忽略了一個取決於其中一個類的東西。就此而言。

在進行重大重構時,您的測試會發生很大變化。 有時候只需要進行概念性測試就可以了,所以你可以確保使用重構可用性是相似的,或者你會知道什么樣的測試被棄用,需要在許多其他依賴類中更新。

按照您希望的方式寫出接口,然后針對該接口編寫單元測試。

然后從接口調用遺留代碼,直到測試通過。

然后根據需要重構。

對?

在您開始更改之前,單元測試將作為該方法所需/執行的功能的活動/生活記錄。

在重構方法之后考慮它們就像檢查清單一樣,以確保它在重構之前仍然涵蓋了它所覆蓋的內容。

暫無
暫無

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

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