隨著 PHP 從一種簡單的指令碼語言轉變為一種成熟的程式設計語言,一個典型的 PHP 應用程式的程式碼程式庫的複雜性也隨之增大。為了控制對這些應用程式的支援和維護,我們可以使用各種測試載入器來自動化該流程。其中一種是單元測試,它允許您直接測試所編寫代碼的正確性。然而,通常遺留程式碼程式庫是不適合進行這種測試的。本文將介紹對包含常見問題的 PHP 代碼的重構策略,以便簡化使用流行的單元測試工具進行測試的過程,同時減少改進程式碼程式庫的依賴性。 簡介 回顧 PHP 15 年的發展曆程,我們發現它已經從一個簡單的用來替代當時流行的 CGI 指令碼的動態指令碼語言變成一種成熟的現代程式設計語言。 隨著程式碼程式庫的增長,手動測試已經變成不可能完成的任務,無論是大是小,所有代碼的變化都會對整個應用程式產生影響。這些影響可能小到只是影響某個頁面的加 載或表單儲存,也可能是產生難以檢測的問題,或者產生只在特定條件下才會出現的錯誤。甚至,它可能會使以前修複的問題重新出現在應用程式中。為此開發了許 多測試載入器來解決這些問題。 其中一種流行的方法是所謂的功能或驗收測試,它會通過應用程式的典型使用者互動來測試這個應用程式。這是一種 很適合測試應用程式中各個進程的方法,但是測試過程可能非常慢,而且一般無法測試底層的類和方法是否按要求正常工作。這時,我們需要使用另一種測試方法, 那就是單元測試。單元測試的目標是測試應用程式底層代碼的功能,保證它們執行後產生正確的結果。通常,這些 “不斷增大” 的 Web 應用程式會慢慢出現越來越多久而久之難以測試的遺留代碼,這使Team Dev很難保證應用程式測試的覆蓋率。這通常被稱為 “不可測試代碼”。現在讓我們看看如何識別應用程式中的不可測試代碼,以及修複這些代碼的方法。 識別不可測試的代碼 關於程式碼程式庫不可測試性的問題域通常在編寫代碼時是不明顯的。當編寫 PHP 應用程式代碼時,人們傾向於按照 Web 請求的流程來編寫代碼,這通常就是在應用程式設計時採用一種更加流程化的方法。急於完成項目或快速修複應用程式都可能促使開發人員 “走捷徑”,以便快速完成編碼。以前,編寫不當或者混亂的代碼可能會加重應用程式中的不可測試性問題,因為開發人員通常會進行風險最小的修複,即使它可能產生後續的支援問題。這些問題域都是無法通過一般的單元測試發現的。 依賴全域狀態的函數 全域變數在 PHP 應用程式中很方便。它們允許您在應用程式中初始化一些變數或對象,然後在應用程式的其他位置使用。然而,這種靈活性是有代價的,過度使用全域變數是不可測試代碼的一個通病。我們可以在 清單 1中看到這種情況。 清單 1. 依賴於全域狀態的函數 results = $dbconn->query('select name from mytable'); } public function getFirstResult() { return $this->results[0]; } } 在這裡,為了測試對象的 fdfdfd 方法,我們最終需要建立一個資料庫連接,給表添加一些記錄,然後在測試之後清除所有這些資源。如果測試 fdfdfd完全不需要這些東西,那麼這個過程可能太過於複雜。因此,我們要修改 清單 6所示的建構函式。 清單 6. 為忽略所有不必要的初始化邏輯而修改的類 init(); } public function init() { $dbconn = new DatabaseConnection('localhost','user','password'); $this->results = $dbconn->query('select name from mytable'); } public function getFirstResult() { return $this->results[0]; } } 我們重構了建構函式中大量的代碼,將它們移到一個 init() 方法中,這個方法預設情況下仍然會被建構函式調用,以避免破壞現有代碼的邏輯。然而,現在我們在測試過程中只能夠傳遞一個布爾值 false 給建構函式,以避免調用 init()方法和所有不必要的初始化邏輯。類的這種重構也會改進代碼,因為我們將初始化邏輯從對象的建構函式分離出來了。 經硬式編碼類依賴性 正如我們在前一節介紹的,造成測試困難的大量類設計問題都集中在初始化各種不需要測試的對象上。在前面,我們知道繁重的初始化邏 輯可能會給測試的編寫造成很大的負擔(特別是當測試完全不需要這些對象時),但是如果我們在測試的類方法中直接建立這些對象,又可能造成另一個問題。清單 7顯示的就是可能造成這個問題的範例程式碼。 清單 7. 在一個方法中直接初始化另一個對象的類 query('select name from user'); sort($results); return $results; } } 假設我們正在測試上面的 getUserList方法,但是我們的測試關注點是保證返回的 使用者清單是按字母順序正確排序的。在這種情況下,我們的問題不在於是否能夠從資料庫擷取這些記錄,因為我們想要測試的是我們是否能夠對返回的記錄進行排 序。問題是,由於我們是在這個方法中直接執行個體化一個資料庫連接對象,所以我們需要執行所有這些繁瑣的操作才能夠完成方法的測試。因此,我們要對方法進行修 改,使這個對象可以在中間插入,如 清單 8所示。 清單 8. 這個類有一個方法會直接執行個體化另一個對象,但是也提供了一種重寫的方法 query('select name from user'); sort($results); return $results; } } 現在您可以直接傳入一個對象,它與預期資料庫連接對象相相容,然後直接使用這個對象,而非建立一個新對象。您也可以傳 入一個類比對象,也就是我們在一些調用方法中,用硬式編碼方式直接返回我們想要的值。在這裡,我們可以類比資料庫連線物件的查詢方法,這樣我們就只需要返 回結果,而不需要真正地去查詢資料庫。進行這樣的重構也能夠改進這個方法,因為它允許您的應用程式在需要時插入不同的資料庫連接,而不是只綁定一個指定的 預設資料庫串連。 可測試代碼的好處 顯然,編寫更具可測試性的代碼肯定能夠簡化 PHP 應用程式的單元測試(正如您在本文展示的例子中所看到的),但是在這個過程中,它也能夠改進應用程式的設計、模組化和穩定性。我們都曾經看到過 “spaghetti” 代碼,它們在 PHP 應用程式的一個主要流程中充斥了大量的業務和表現邏輯,這毫無疑問會給那些使用這個應用程式的人造成嚴重的支援問題。在使代碼變得更具可測試性的過程中, 我們對前面一些有問題的代碼進行了重構;這些代碼不僅設計上有問題,功能上也有問題。通過使這些函數和類的用途更廣泛,以及通過刪除硬式編碼依賴性,我們 使之更容易被應用程式其他部分重用,我們提高了代碼的可重用性。此外,我們還將編寫不當的代碼替換成更優質的代碼,從而簡化將來對程式碼程式庫的支援。 結束語 在本文中,通過 PHP 應用程式中一些典型的不可測試程式碼範例,我們瞭解了如何改進 PHP 代碼的可測試性。我們還介紹了這些情況是如何出現在應用程式中的,然後介紹了如何恰當地修複這些問題代碼來便於進行測試。我們還瞭解了這些代碼的修改不僅 能夠提高代碼的可測試性,也能夠普遍改進代碼的品質,以及提高重構代碼的可重用性。
http://www.bkjia.com/PHPjc/735874.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/735874.htmlTechArticle隨著 PHP 從一種簡單的指令碼語言轉變為一種成熟的程式設計語言,一個典型的 PHP 應用程式的程式碼程式庫的複雜性也隨之增大。為了控制對這些應用程...