類型或測試:為什麼不呢?
關於類型化JavaScript的價值,時不時就會出現一場辯論。 “多寫些測試!”一些反對者喊道。 “用類型替換單元測試!”其他人大聲疾呼。兩者在某些方面是對的,在另一些方面是錯的。 Twitter的空間不足以體現細微之處。但在本文中,我們可以嘗試闡述一個合理的論點,說明兩者如何以及應該如何共存。
正確性:我們真正想要的
最好從結果開始。我們從所有這些元工程最終真正想要的是正確性。我不是指嚴格的理論計算機科學定義,而是程序行為與其規範的更普遍的遵守:我們腦海中對程序的工作方式有一個想法,而編程過程則組織位和字節,使這個想法成為現實。因為我們並不總是精確地知道我們想要什麼,而且我們希望確信我們的程序在進行更改時不會中斷,所以我們在現有的原始代碼之上編寫類型和測試,僅僅是為了讓事情首先能夠工作。
因此,如果我們接受正確性是我們想要的,而類型和測試只是實現這一目標的自動化方法,那麼最好有一個直觀的模型來展示類型和測試如何幫助我們實現正確性,從而理解它們在哪裡重疊以及在哪里相互補充。
程序正確性的可視化模型
如果我們將程序可能執行的所有操作(包括錯誤)的整個無限圖靈完備可能空間想像為一個巨大的灰色區域,那麼我們希望程序執行的操作,我們的規範,是該可能空間的一個非常非常小的子集(下圖中的綠色菱形,為了顯示某些內容而誇大了大小):
我們在編程中的工作是盡可能地將我們的程序與規範協調一致(當然,我們知道我們是不完美的,我們的規範不斷變化,例如由於人為錯誤、新功能或未指定的行為;因此我們從未完全設法實現完全重疊):
再次注意,為了我們在此處的討論目的,程序行為的邊界也包括計劃中和計劃外的錯誤。我們對“正確性”的含義包括計劃中的錯誤,但不包括計劃外的錯誤。
測試與正確性
我們編寫測試以確保我們的程序符合我們的期望,但有許多需要測試的內容的選擇:
理想的測試是圖中的橙色點——它們準確地測試了我們的程序是否與規範重疊。在這個可視化中,我們並沒有真正區分測試類型,但您可以將單元測試想像成非常小的點,而集成/端到端測試是大的點。無論哪種方式,它們都是點,因為沒有一個測試可以完全描述程序中的每條路徑。 (事實上,您可以擁有100%的代碼覆蓋率,但仍然無法測試每條路徑,因為存在組合爆炸!)
圖中的藍色點是一個糟糕的測試。當然,它測試了我們的程序是否有效,但它實際上並沒有將其固定到底層規範(歸根結底,我們真正想要從程序中獲得的東西)。當我們修復程序以更緊密地與規範對齊時,此測試就會中斷,給我們一個誤報。
紫色點是一個有價值的測試,因為它測試了我們認為程序應該如何工作,並確定了程序當前沒有工作的區域。領先使用紫色測試並相應地修復程序實現也被稱為測試驅動開發。
圖中的紅色測試是一個罕見的測試。它不是測試“快樂路徑”(包括計劃的錯誤狀態)的普通(橙色)測試,而是一個期望並驗證“不快樂路徑”失敗的測試。如果此測試在應該“失敗”的地方“通過”,則這是一個巨大的早期警告信號,表明出了問題——但編寫足夠的測試來覆蓋規範綠色區域之外存在的巨大可能的不快樂路徑基本上是不可能的。人們很少發現測試那些不應該工作的東西不工作有價值,所以他們不做;但當事情出錯時,它仍然可以是一個有幫助的早期警告信號。
類型與正確性
如果測試是程序可能執行操作的可能性空間上的單個點,則類型表示從總可能性空間中分割整個部分的類別。我們可以將它們可視化為矩形:
我們選擇一個矩形來對比表示程序的菱形,因為沒有一個類型系統本身可以使用類型來完全描述我們的程序行為。 (要舉一個簡單的例子,一個應該始終為正整數的id是一個數字類型,但數字類型也接受分數和負數。除了非常簡單的數字文字聯合之外,無法將數字類型限制在特定範圍內。)
類型在您編寫代碼時充當程序可以運行位置的約束。如果我們的程序開始超過程序類型的指定邊界,我們的類型檢查器(如TypeScript或Flow)將簡單地拒絕讓我們編譯程序。這很好,因為在像JavaScript這樣的動態語言中,很容易意外地創建一個肯定不是您想要創建的崩潰程序。最簡單的附加值是自動空值檢查。如果foo沒有名為bar的方法,則調用foo.bar()將導致眾所周知的undefined is not a function運行時異常。如果foo被完全類型化,則可以在編寫時由類型檢查器捕獲此問題,並具體指出有問題的代碼行(並具有自動完成的伴隨好處)。這是測試根本無法做到的事情。
我們可能希望為我們的程序編寫嚴格的類型,就好像我們試圖編寫仍然符合我們規範的盡可能小的矩形一樣。但是,這有一個學習曲線,因為充分利用類型系統涉及學習全新的語法和運算符以及泛型類型邏輯的語法,這些語法和邏輯是模擬JavaScript的完整動態範圍所必需的。手冊和備忘單有助於降低學習曲線,並且這裡需要更多投資。
幸運的是,這個採用/學習曲線不必阻止我們。由於類型檢查是Flow的可選過程,並且TypeScript的可配置嚴格性(能夠選擇性地忽略有問題的代碼行),我們可以從類型安全的範圍內進行選擇。我們甚至可以對此進行建模:
較大的矩形,如上圖中的大紅色矩形,表示對代碼庫的類型系統的非常寬鬆的採用——例如,允許implicitAny並完全依賴類型推斷來僅僅將我們的程序從最糟糕的編碼中限制出來。
中等嚴格性(如中等大小的綠色矩形)可以表示更忠實的類型化,但有很多漏洞,例如在整個代碼庫中使用顯式any實例和手動類型斷言。儘管如此,即使進行這種輕量級的類型化工作,與我們的規範不匹配的有效程序的可能表面積也會大大減少。
最大嚴格性,如紫色矩形,使事情與我們的規範非常緊密,以至於它有時會發現您的程序中不符合的部分(這些通常是程序行為中計劃外的錯誤)。像這樣在現有程序中查找錯誤是將普通JavaScript代碼庫轉換的團隊中非常常見的案例。但是,從我們的類型檢查器中獲得最大的類型安全性可能需要利用泛型類型和特殊運算符,這些運算符旨在為每個變量和函數細化和縮小可能的類型空間。
請注意,我們不必在編寫類型之前先編寫程序。畢竟,我們只是希望我們的類型能夠密切模擬我們的規範,因此我們實際上可以先編寫我們的類型,然後稍後再填充實現。理論上,這將是類型驅動開發;在實踐中,很少有人真正以這種方式開發,因為類型與我們的實際程序代碼緊密地滲透和交織在一起。
將它們放在一起
我們最終要建立的是一個直觀的可視化,說明類型和測試如何在保證程序的正確性方面相互補充。
我們的測試斷言我們的程序在選定的關鍵路徑中專門按預期執行(儘管如上所述,存在某些其他測試變體,但絕大多數測試都這樣做)。在我們已經開發的可視化語言中,它們將程序的深綠色菱形“固定”到規範的淺綠色菱形上。程序的任何移動都會破壞這些測試,這會使它們發出警報。這很棒!測試也具有無限的靈活性和可配置性,適用於最自定義的用例。
我們的類型斷言我們的程序不會脫離我們的控制,方法是禁止超出我們繪製的邊界的可能故障模式,希望盡可能緊密地圍繞我們的規範。在我們可視化語言中,它們“包含”程序偏離規範的可能漂移(因為我們總是存在缺陷,我們犯的每一個錯誤都會為我們的程序增加額外的失敗行為)。類型也是鈍的,但功能強大的(因為類型推斷和編輯器工具)工具,受益於強大的社區提供的您不必從頭開始編寫的類型。
簡而言之:
- 測試最擅長確保快樂路徑有效。
- 類型最擅長防止不快樂路徑存在。
根據它們的優勢將它們一起使用,以獲得最佳結果!
如果您想了解更多關於類型和測試如何交叉的信息,Gary Bernhardt關於邊界的精彩演講和Kent C. Dodds的測試獎杯對我的這篇文章的思考產生了重大影響。
以上是類型或測試:為什麼不呢?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)