溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

web開發(fā)如何進(jìn)行單元測試

發(fā)布時間:2022-10-11 09:39:30 來源:億速云 閱讀:139 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“web開發(fā)如何進(jìn)行單元測試”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強(qiáng),希望這篇“web開發(fā)如何進(jìn)行單元測試”文章能幫助大家解決問題。

軟件

軟件是可以改變的。這就是為什么它被稱為「軟」件,它的可塑性是比硬件強(qiáng)的。一個優(yōu)秀的工程師團(tuán)隊?wèi)?yīng)該是一個公司一筆驚人的財富,他們編寫可以隨著業(yè)務(wù)發(fā)展而不斷增值的系統(tǒng)。

那么,為什么我們在這方面做的如此糟糕呢?你聽說過多少完全失敗的項目?或者成為「遺產(chǎn)」,必須完全重寫 (重寫通常也會失敗! )

軟件系統(tǒng)是如何“失敗”的呢?難道不能在它正確之前進(jìn)行修改嗎?這就是我們的承諾!

很多人選擇用 Go 來構(gòu)建系統(tǒng),因為它已經(jīng)做出了許多選擇,人們希望這些選擇能讓它更經(jīng)得起遺產(chǎn)的考驗。

  • 和我之前 Scala 生涯相比 我形容它簡直會讓你有上吊的沖動,Go 只有25個關(guān)鍵詞和 很多 可以從標(biāo)準(zhǔn)庫和一些其他小型庫中構(gòu)建的系統(tǒng)。 愿景是通過 Go 你可以編寫代碼并在6個月內(nèi)回顧它,它仍然有意義。

  • 與大多數(shù)替代品相比,測試,基準(zhǔn)測試,語義解析和裝載方面的工具是一流的。

  • 很棒的標(biāo)準(zhǔn)庫。

  • 嚴(yán)謹(jǐn)?shù)姆答伝芈纷尵幾g非常迅速

  • Go 有向后兼容性承諾。看起來 Go 將來會獲得泛型和其他功能,但設(shè)計師們已經(jīng)承諾,即使你 5 年前寫的 Go 代碼仍然會構(gòu)建。我花了幾周的時間將項目從Scala 2.8 升級到 2.10。

即使擁有所有這些優(yōu)秀的屬性,我們?nèi)匀豢赡軙圃斐鲈愀獾南到y(tǒng),因此我們應(yīng)不論你使用的語言優(yōu)秀與否,你都應(yīng)該回顧過去的軟件工程并理解其中獲得的經(jīng)驗教訓(xùn)。

1974年,一位名叫 [曼尼·雷曼]的聰明的軟件工程師寫下了 雷曼軟件進(jìn)化定律。

這些定律描述了推動新發(fā)展的力量和阻礙進(jìn)步的力量之間的平衡。

如果我們不希望開發(fā)的系統(tǒng)變成遺產(chǎn),被一遍又一遍的重寫,那么這些力量是我們需要著重理解的。

連續(xù)變化規(guī)律

實際生活中被使用的軟件系統(tǒng)都必須不斷的改變,要不然就會被大環(huán)境淘汰

很明顯,一個系統(tǒng) 必須 不斷的改變,要不然就會變的越來越?jīng)]用,但是這種情況為什么經(jīng)常被忽略呢?

因為很多開發(fā)團(tuán)隊在指定的日期交付一個項目會得到獎金,然后他們會繼續(xù)開發(fā)下一個項目。如果這個軟件是「幸運的」,至少它會以某種形式移交給另一組人來維護(hù)它,但是他們一定不會繼續(xù)迭代它。

人們通常關(guān)心的是選擇一個框架來幫助他們「快速交付」,而不是關(guān)注系統(tǒng)持久性發(fā)展。

即使你是一名出色的軟件工程師,你仍然會因為不了解自己系統(tǒng)的未來需求而成為受害者。隨著業(yè)務(wù)的變化,你寫的出色的代碼也會變得不再適用。

雷曼在70年代很成功,因為他給了我們另一條值得深思的規(guī)律。

復(fù)雜性增加的規(guī)律

隨著系統(tǒng)的發(fā)展,除非采取措施減少系統(tǒng)復(fù)雜性的增加,否則系統(tǒng)的復(fù)雜程度會持續(xù)增加

他現(xiàn)在要所說的就是:我們不能讓軟件團(tuán)隊成為純粹的功能工廠,只是通過將越來越多的功能集中到軟件上, 來讓系統(tǒng)能夠繼續(xù)長期運行。

隨著我們知識領(lǐng)域的變化,我們 必須 持續(xù)管理系統(tǒng)的復(fù)雜性。

重構(gòu)

軟件的開發(fā)可以在 許多 方面保持軟件的可塑性,例如:

  • 開發(fā)人員授權(quán)

  • 通?!负谩沟拇a。關(guān)注代碼合理的分離,等等

  • 溝通能力

  • 體系結(jié)構(gòu)

  • 可觀性

  • 可部署性

  • 自動化測試

  • 閉環(huán)

我將重點放在重構(gòu)上。在開發(fā)人員編程的第一天經(jīng)常聽到的話就是「我們需要重構(gòu)它」。

這句話從和而來?重構(gòu)與編寫代碼有什么不同?

我知道我和其他很多人都 認(rèn)為 我們在進(jìn)行重構(gòu),但我們錯了。

馬丁·福勒描述了人們是如何犯錯的

然而「重構(gòu)」經(jīng)常被用在不合適的地方。如果有人討論一個系統(tǒng)在重構(gòu)時出現(xiàn)了幾天故障,你可以肯定他們不是在重構(gòu)。

那是什么呢?

因式分解

在學(xué)校學(xué)習(xí)數(shù)學(xué)時,你可能學(xué)了因式分解。這里有一個非常簡單的例子

計算 1/2 + 1/4

為此,將分母 分解,將表達(dá)式轉(zhuǎn)換為

2/4 + 1/4 你可以把它變成 3/4.

我們可以從中吸取一些重要的教訓(xùn)。當(dāng)我們 分解表達(dá)式 時,我們沒有改變表達(dá)式的含義。兩者都等于 3/4,但我們通過將 1/2 變?yōu)?2/4 后,我們的工作變得更容易了;它更適合我們的「領(lǐng)域」。

當(dāng)你重構(gòu)代碼時,你應(yīng)該在「符合」你當(dāng)前系統(tǒng)需求的情況下,嘗試找到一個方法來使你的代碼更容易理解。關(guān)鍵是你不應(yīng)該改變代碼原有的行為.

Go 的一個例子

下面這個方法是用特定的 language 問候 name

func Hello(name, language string) string {
    if language == "es" {
        return "Hola, " + name
    }

    if language == "fr" {

        return "Bonjour, " + name

    }

    // 想象一下更多的語言

    return "Hello, " + name

}

如果有幾十個 if 語句會讓人感覺不舒服,而且我們還要重復(fù)的使用特定的 language 去伴隨著 , 問候 name。因此,我們來重構(gòu)代碼。

func Hello(name, language string) string {
      return fmt.Sprintf(
          "%s, %s",
          greeting(language),
          name,
      )
}

var greetings = map[string]string {
  es: "Hola",
  fr: "Bonjour",
  //等等...
}

func greeting(language string) string {
  greeting, exists := greetings[language]

  if exists {
     return greeting
  }

  return "Hello"
}

實際上,這個重構(gòu)的本質(zhì)并不重要,重要的是我們沒有改變代碼的行為。

當(dāng)重構(gòu)時,你可以做任何你喜歡的事情,添加接口,新類型,函數(shù),方法等等。唯一的規(guī)則是你不能改變代碼的行為。

重構(gòu)代碼時不要改變功能

這非常重要。如果你重構(gòu)時改變功能,你相當(dāng)于同時在做 件事。作為軟件工程師,我們應(yīng)該學(xué)習(xí)把系統(tǒng)分成不同的文件/包/功能/等等,因為我們知道試圖理解一大塊東西是困難的。

我們不要一次想很多事情,因為那會使我們犯錯誤。我目睹了許多重構(gòu)工作的失敗,因為開發(fā)人員貪多嚼不爛。

當(dāng)我在數(shù)學(xué)課上用筆和紙做因式分解時,我必須手動檢查我是否改變了頭腦中表達(dá)式的意思。當(dāng)我們重構(gòu)代碼時,尤其是在一個重要的系統(tǒng)上,我們?nèi)绾沃牢覀兪欠窀淖兞斯δ?

那些選擇不編寫測試的人通常依賴于手動測試。除非是一個小項目,要不然這將是一個巨大的時間消耗,并且從長遠(yuǎn)來看不利于系統(tǒng)將來的擴(kuò)展。

為了安全地重構(gòu),您需要單元測試因為它提供了

  • 可以在不擔(dān)心功能改變的情況下重構(gòu)代碼

  • 有助于開發(fā)人員編寫關(guān)于系統(tǒng)應(yīng)該如何運行的文檔

  • 比手工測試更快更可靠的反饋

Go 的一個例子

我們有一個 Hello 方法的單元測試是這樣的:

func TestHello(t *testing.T) {

    got := Hello("Chris", es)

    want := "Hola, Chris"

    if got != want {

        t.Errorf("got %q want %q", got, want)

    }

}

在命令行中,我們可以運行 go test ,并且可以立即得到我們的重構(gòu)工作是否影響了原來程序運行的反饋。實際上,最好學(xué)習(xí)在編輯器/IDE中運行測試。

你想要得到你程序正在運行的狀態(tài)

  • 小的重構(gòu)

  • 運行測試

  • 重復(fù)

所有這些都在一個非常緊密的反饋回路中,這樣你就不會犯錯誤。

在一個項目中,你所有的關(guān)鍵行為都經(jīng)過了單元測試,并且在一秒鐘內(nèi)給出反饋,這是一個非常強(qiáng)大的安全網(wǎng),可以在你需要的時候進(jìn)行大膽的重構(gòu)。這有助于我們管理雷曼所描述的日益增長的復(fù)雜性。

單元測試這么的優(yōu)秀,為什么有時候編寫它們會受到阻力呢?

一方面,有人(像我一樣)說單元測試對你的系統(tǒng)的長期健康很重要,因為它們確保你可以自信地繼續(xù)重構(gòu)。

另一方面,有人說單元測試實際上 阻礙 了重構(gòu)。

捫心自問,重構(gòu)時需要多久更改一次測試?這些年來,我參與了許多測試覆蓋率非常高的項目,但是工程師們不愿意重構(gòu),因為他們認(rèn)為更改測試是費力的事情。

這與我們的承諾相反!

為什么會這樣?

假設(shè)你被要求去畫一個正方形,我們認(rèn)為最好的方法是把兩個三角形粘在一起。


我們在正方形周圍寫單元測試以確保兩邊相等然后我們在三角形周圍寫一些測試。我們想確保我們的三角形渲染正確所以我們斷言這些角之和是180度,我們做了兩個測試來檢查,等等。測試覆蓋率非常重要,編寫這些測試非常簡單,為什么不呢?

幾周后,不斷變化的規(guī)律沖擊了我們的系統(tǒng),一個新的開發(fā)人員做出了一些改變。她現(xiàn)在認(rèn)為,如果用兩個矩形來構(gòu)成正方形會比兩個三角形更好。


他嘗試進(jìn)行這種重構(gòu),并從一些失敗的測試中得到一些提示。他真的破壞了代碼重要的功能嗎?她現(xiàn)在必須深入研究這些三角形的測試,并試圖理解它內(nèi)部到底發(fā)生了什么。

正方形由三角形組成實際上并不重要,但是我們的測試錯誤地提高了實現(xiàn)細(xì)節(jié)的重要性 。

測試功能而不是實現(xiàn)細(xì)節(jié)

當(dāng)我聽到人們抱怨單元測試時,通常是因為測試處于錯誤的抽象級別。他們都在測試實現(xiàn)細(xì)節(jié),過度的觀察協(xié)作者的代碼并進(jìn)行許多嘲諷。

我相信這個問題是因為他們對單元測試的誤解,以及對度量標(biāo)準(zhǔn)(測試覆蓋率)的追求。

如果我說的只是測試功能,我們不應(yīng)該只編寫系統(tǒng)/黑盒測試嗎?在驗證關(guān)鍵用戶歷程方面,這類測試確實有很多價值,但它們通常編寫成本高,運行速度慢。由于這個原因,它們對 重構(gòu) 沒有太大幫助,因為反饋循環(huán)很慢。此外,與單元測試相比,黑盒測試對解決根本問題并沒有太大幫助。

那什么 正確的抽象級別呢?

編寫有效的單元測試是一個設(shè)計問題

暫時忘記測試,最好在您的系統(tǒng)中包含自包含的、解耦的「單元」,以您的領(lǐng)域中的關(guān)鍵概念為中心。

我喜歡將這些單元想象成簡單的樂高積木,它們具有一致的 API,我可以將這些 API 與其他積木結(jié)合起來構(gòu)建更大的系統(tǒng)。在這些 API 的內(nèi)部,可能有許多東西(類型、函數(shù)等)協(xié)作,使它們能夠按照需要工作。
例如,如果你使用 Go 開發(fā)一個銀行系統(tǒng),你應(yīng)該會有一個「賬戶」包。它將提供一個不會泄漏實現(xiàn)細(xì)節(jié)且易于集成的 API。

如果您的單元遵循了這些屬性,那么你可以針對它們的公共 API 編寫單元測試。根據(jù)定義,這些測試只能測試有用的功能。在有這些單元的基礎(chǔ)下,只要有需要,我們可以自由地實現(xiàn)重構(gòu),并且在大多數(shù)情況下測試不會成為我們的阻礙。

這些是單元測試嗎?

是的。單元測試是針對我所描述的「單元」的。它們 從不 只針對一個類/函數(shù)/任何東西。

將這些概念結(jié)合起來

我們已經(jīng)講過了

  • 重構(gòu)

  • 單元測試

  • 單元設(shè)計

我們可以看到的是,這些方面的軟件設(shè)計是相輔相成的。

重構(gòu)

  • 為我們的單元測試提供信號。如果我們不得不手動檢查,那么我們需要更多的測試。如果測試失敗,那么我們的測試就處于錯誤的抽象級別(或者沒有值,應(yīng)該刪除)

  • 幫助我們處理單元內(nèi)部和單元之間的復(fù)雜性。

單元測試

  • 為重構(gòu)提供了安全防護(hù)。

  • 驗證并記錄我們單元的功能。

(精心設(shè)計的) 單元

  • 易于編寫 有意義 的單元測試。

  • 易于重構(gòu)。

是否有一個流程可以幫助我們不斷地重構(gòu)代碼,以管理復(fù)雜性并保持系統(tǒng)的可伸縮性?

為什么要測試驅(qū)動開發(fā)(TDD)

一些人可能會因為雷曼關(guān)于如何讓軟件不斷改變的思想,因此過度的設(shè)計,浪費大量的時間提前嘗試創(chuàng)建「完美的」可擴(kuò)展系統(tǒng),結(jié)果卻一事無成。

在過去糟糕的軟件時代,分析師團(tuán)隊將花費6個月的時間編寫需求文檔,架構(gòu)師團(tuán)隊將花費6個月的時間進(jìn)行設(shè)計,幾年后整個項目就失敗了。

我說過去的日子很糟糕,但現(xiàn)在還是這樣!

敏捷開發(fā)告訴我們,我們需要迭代地工作,從小處著手并不斷改進(jìn)軟件,這樣我們就可以快速地得到關(guān)于軟件設(shè)計的反饋,以及軟件如何與實際用戶一起工作;TDD強(qiáng)制執(zhí)行這種方法。

TDD通過鼓勵一種不斷重構(gòu)和迭代交付的開發(fā)方式,解決了雷曼所談?wù)摰亩珊推渌麣v史上難以學(xué)到的教訓(xùn)。

小步驟

  • 為小功能寫一個小測試

  • 檢查測試是否失敗,并有明顯的錯誤(紅色)

  • 編寫最少的代碼使測試通過(綠色)

  • 重構(gòu)

  • 重復(fù)以上步驟

隨著你熟練程度的挺高,對于你來說這會變?yōu)橐环N很自然的工作方式,工作效率也會越來越高

你開始希望你的小測試單元完成整個測試不要花費太多時間,因為如果你看到你的程序一直處于非「綠色」?fàn)顟B(tài),那表明你有可能遇到了一點小麻煩。

有了這些測試反饋,你能輕松保證一些小型應(yīng)用功能的穩(wěn)定性。

關(guān)于“web開發(fā)如何進(jìn)行單元測試”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI