日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

go定时器 每天重复_通过测试学习Go:Hello, World

發布時間:2023/11/27 生活经验 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go定时器 每天重复_通过测试学习Go:Hello, World 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方藍色“Go語言中文網”關注我們,設個星標,每天學習?Go?語言

你可以在這里查看本章的所有代碼[1]

按照傳統,我們學習新語言編寫的第一個程序都是 Hello,world。

在之前的章節[2]中,我們討論了 Go 死板的文件存放位置。

按照以下路徑創建目錄?$GOPATH/src/github.com/{your-user-id}/hello

如果你使用的是基于 Unix 的系統,你的名字是「bob」并且很樂于遵循 Go 關于?$GOPATH?的約定(這是最簡單的設置方式)。那么你可以執行以下命令?mkdir -p $GOPATH/src/github.com/bob/hello?快速創建目錄。

在該目錄下創建一個?hello.go?的文件并寫入以下代碼,鍵入?go run hello.go?來運行程序。

package main

import "fmt"

func main() {
fmt.Println("Hello, world")
}

它是如何運行的

當你使用 Go 編寫程序時,你將定義一個?main?包,并在其中定義一個?main?函數。包是一種將相關的 Go 代碼組合到一起的方式。

func?關鍵字通過一個名稱和函數體來定義函數。

通過?import "fmt"?導入一個包含?Println?函數的包,我們用它來打印輸出。

如何測試

你會如何測試這個程序?將你「領域」內的代碼和外部世界(會引起副作用)分離開會更好。fmt.Println?會產生副作用(打印到標準輸出),我們發送的字符串在自己的領域內。[^注1]

[^注1]: 原文: How do you test this? It is good to separate your "domain" code from the outside world (side-effects). The?fmt.Println?is a side effect (printing to stdout) and the string we send in is our domain.

所以為了更容易測試,我們把這些問題拆分開。

package main

import "fmt"

func Hello() string {
return "Hello, world"
}

func main() {
fmt.Println(Hello())
}

我們再次使用?func?創建了一個新函數,但是這次我們在定義中添加了另一個關鍵字?string。這意味著這個函數返回一個字符串。

現在創建一個名為?hello_test.go?的新文件,我們將在這里為?Hello?函數編寫一個測試

package main

import "testing"

func TestHello(t *testing.T) {
got := Hello()
want := "Hello, world"

if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
}

在解釋之前,讓我們先運行一下代碼。在終端運行?go test,它應該已經通過了!為了檢驗測試,可以嘗試通過改變?want?字符串來破壞測試。

注意,你不必在多個測試框架之間進行選擇,然后理解如何安裝它們。你需要的一切都內建在語言中,語法與你將要編寫的其余代碼相同。

編寫測試

編寫測試和寫函數很類似,其中有一些規則

  • 程序需要在一個名為?xxx_test.go?的文件中編寫

  • 測試函數的命名必須以單詞?Test?開始

  • 測試函數只接受一個參數?t *testing.T

現在這些信息足以讓我們明白,類型為?*testing.T?的變量?t?是你在測試框架中的 "hook"(鉤子),所以當你想讓測試失敗時可以執行?t.Fail()?之類的操作。

我們再討論一些新的話題:

if

Go 的?if?語句非常類似于其他編程語言。

聲明變量

我們使用?varName := value?的語法聲明變量,它允許我們在測試中重用一些值使代碼更具可讀性。

t.Errorf

我們調用?t?的?Errorf?方法打印一條消息并使測試失敗。f?表示格式化,它允許我們構建一個字符串,并將值插入占位符值?%s?中。當你測試失敗時,它能夠讓你清楚測試是如何運行的。

稍后我們將探討方法和函數之間的區別。

Go 文檔

Go 的另一個高質量特征是文檔。通過運行?godoc -http :8000,可以在本地啟動文檔。如果你訪問?localhost:8000/pkg[3],將看到系統上安裝的所有包。

大多數標準庫都有優秀的文檔和示例。瀏覽?http://localhost:8000/pkg/testing/[4]?是非常值得的,去看一下你有什么可以用的。

Hello, YOU

現在有了測試,就可以安全地迭代我們的軟件了。

在上一個示例中,我們在寫好代碼?之后?編寫了測試,以便你學會如何編寫測試和聲明函數。從此刻起,我們將?首先編寫測試

我們的下一個需求是讓我們指定問候的接受者。

讓我們從在測試中捕獲這些需求開始。這是基本的測試驅動開發,可以確保我們的測試用例?確實?是測試我們想要的。當你回顧編寫測試時,存在一個風險:即使代碼沒有按照預期工作,測試也可能繼續通過。

package main

import "testing"

func TestHello(t *testing.T) {
got := Hello("Chris")
want := "Hello, Chris"

if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
}

這時運行?go test,你應該會獲得一個編譯錯誤

./hello_test.go:6:18: too many arguments in call to Hello
have (string)
want ()

當使用像 Go 這樣的靜態類型語言時,聆聽編譯器?是很重要的。編譯器理解你的代碼應該如何拼接到一起工作,所以你就不必關心這些了。

在這種情況下,編譯器告訴你需要怎么做才能繼續。我們必須修改函數?Hello?來接受一個參數。

修改?Hello?函數以接受字符串類型的參數

func Hello(name string) string {
return "Hello, world"
}

如果你嘗試再次運行測試,main.go?將無法編譯,因為你沒有傳遞參數。傳遞參數 "world" 讓它通過。

func main() {
fmt.Println(Hello("world"))
}

現在,當你運行測試時,你應該看到類似的內容

hello_test.go:10: got 'Hello, world' want 'Hello, Chris''

我們最終得到了一個可編譯的程序,但是根據測試它并沒有達到我們的要求。

為了使測試通過,我們使用?name?參數并用?Hello,?字符串連接它,

func Hello(name string) string {
return "Hello, " + name
}

現在再運行測試就應該通過了。通常作為 TDD 周期的一部分,我們該著手?重構?了。

關于版本控制的一點說明

此時,如果你正在使用版本控制(你應該這樣做!)我將按原樣?提交?代碼。因為我們擁有一個測試支持的可用軟件。

不過我不會推送到主分支上,因為我下一步計劃重構。現在提交很合適,當重構中陷入混亂時你總是可以回到可用版本。

這里沒有太多可重構的,但我們可以介紹一下另一種語言特性?常量

常量

通常我們這樣定義一個常量

const helloPrefix = "Hello, "

現在我們可以重構代碼

const helloPrefix = "Hello, "

func Hello(name string) string {
return helloPrefix + name
}

重構之后,重新測試,以確保沒有破壞任何東西。

常量應該可以提高應用程序的性能,它避免了每次調用?Hello?時創建?"Hello, "?字符串實例。

顯然,對于這個例子來說,性能提升是微不足道的!但是創建常量的價值是可以快速理解值的含義,有時還可以幫助提高性能。

再次回到 Hello, world

下一個需求是當我們的函數用空字符串調用時,它默認為打印 "Hello, World" 而不是 "Hello, "

首先編寫一個新的失敗測試

func TestHello(t *testing.T) {

t.Run("saying hello to people", func(t *testing.T) {
got := Hello("Chris")
want := "Hello, Chris"

if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
})

t.Run("say hello world when an empty string is supplied", func(t *testing.T) {
got := Hello("")
want := "Hello, World"

if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
})

}

這里我們將介紹測試庫中的另一個工具 -- 子測試。有時,對一個「事情」進行分組測試,然后再對不同場景進行子測試非常有效。

這種方法的好處是,你可以建立在其他測試中也能夠使用的共享代碼。

當我們檢查信息是否符合預期時,會有重復的代碼。

重構不?僅僅?是針對程序的代碼!

重要的是,你的測試?清楚地說明?了代碼需要做什么。

我們可以并且應該重構我們的測試。

func TestHello(t *testing.T) {

assertCorrectMessage := func(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
}

t.Run("saying hello to people", func(t *testing.T) {
got := Hello("Chris")
want := "Hello, Chris"
assertCorrectMessage(t, got, want)
})

t.Run("empty string defaults to 'world'", func(t *testing.T) {
got := Hello("")
want := "Hello, World"
assertCorrectMessage(t, got, want)
})

}

我們在這里做了什么?

我們將斷言重構為函數。這減少了重復并且提高了測試的可讀性。在 Go 中,你可以在其他函數中聲明函數并將它們分配給變量。你可以像調用普通函數一樣調用它們。我們需要傳入?t *testing.T,這樣我們就可以在需要的時候令測試代碼失敗。

t.Helper()?需要告訴測試套件這個方法是輔助函數(helper)。通過這樣做,當測試失敗時所報告的行號將在函數調用中而不是在輔助函數內部。這將幫助其他開發人員更容易地跟蹤問題。如果你仍然不理解,請注釋掉它,使測試失敗并觀察測試輸出。

現在我們有了一個寫得很好的失敗測試,讓我們使用?if?修復代碼。

const helloPrefix = "Hello, "

func Hello(name string) string {
if name == "" {
name = "World"
}
return helloPrefix + name
}

如果我們運行測試,應該看到它滿足了新的要求,并且我們沒有意外地破壞其他功能。

回到版本控制

現在我們對代碼很滿意,我將修改之前的提交,所以我們只提交認為好的版本及其測試。

規律

讓我們再次回顧一下這個周期

  • 編寫一個測試

  • 讓編譯通過

  • 運行測試,查看失敗原因并檢查錯誤消息是很有意義的

  • 編寫足夠的代碼以使測試通過

  • 重構

從表面上看,這可能看起來很乏味,但堅持反饋循環非常重要。

它不僅確保你有?相關的測試,還可以確保你通過重構測試的安全性來?設計優秀的軟件

查看測試失敗是一個重要的檢查手段,因為它還可以讓你看到錯誤信息。作為一名開發人員,如果測試失敗時不能清楚地說明問題所在,那么使用這個代碼庫可能會非常困難。

通過確保你的測試?快速?并建立你的工具,以便運行測試足夠簡單,你在編寫代碼時就可以進入流暢的狀態。

如果不寫測試,你提交的時候通過運行軟件來手動檢查你的代碼,這會打破你的流暢狀態,而且你任何時候都無法將自己從這種狀態中拯救出來,尤其是從長遠來看。

繼續前進!更多需求

天吶,我們有更多的需求了。我們現在需要支持第二個參數,指定問候的語言。如果一種我們不能識別的語言被傳進來,就默認為英語。

通過 TDD 輕松實現這一功能,我們是有信心的!

為使用西班牙語的用戶編寫測試,將其添加到現有的測試用例中。

    t.Run("in Spanish", func(t *testing.T) {
got := Hello("Elodie", "Spanish")
want := "Hola, Elodie"
assertCorrectMessage(t, got, want)
})

記住不要欺騙自己!先編寫測試。當你嘗試運行測試時,編譯器?應該?會出錯,因為你用兩個參數而不是一個來調用?Hello

./hello_test.go:27:19: too many arguments in call to Hello
have (string, string)
want (string)

通過向?Hello?添加另一個字符串參數來修復編譯問題

func Hello(name string, language string) string {
if name == "" {
name = "World"
}
return helloPrefix + name
}

當你嘗試再次運行測試時,它會抱怨在其他測試和?main.go?中沒有傳遞足夠的參數給?Hello

./hello.go:15:19: not enough arguments in call to Hello
have (string)
want (string, string)

通過傳遞空字符串來修復它們。現在,除了我們的新場景外,你的所有測試都應該編譯并通過

hello_test.go:29: got 'Hola, Elodie' want 'Hello, Elodie'

這里我們可以使用?if?檢查語言是否是「西班牙語」,如果是就修改信息

func Hello(name string, language string) string {
if name == "" {
name = "World"
}

if language == "Spanish" {
return "Hola, " + name
}

return helloPrefix + name
}

測試現在應該通過了。

現在是?重構?的時候了。你應該在代碼中看出了一些問題,其中有一些重復的「魔術」字符串。自己嘗試重構它,每次更改都要重新運行測試,以確保重構不會破壞任何內容。

const spanish = "Spanish"
const helloPrefix = "Hello, "
const spanishHelloPrefix = "Hola, "

func Hello(name string, language string) string {
if name == "" {
name = "World"
}

if language == spanish {
return spanishHelloPrefix + name
}

return helloPrefix + name
}

法語

  • 編寫一個測試,斷言如果你傳遞?"French"?你會得到?"Bonjour, "

  • 看到它失敗,檢查易讀的錯誤消息

  • 在代碼中進行最小的合理更改

你可能寫了一些看起來大致如此的東西

func Hello(name string, language string) string {
if name == "" {
name = "World"
}

if language == spanish {
return spanishHelloPrefix + name
}

if language == french {
return frenchHelloPrefix + name
}

return helloPrefix + name
}

switch

當你有很多?if?語句檢查一個特定的值時,通常使用?switch?語句來代替。如果我們希望稍后添加更多的語言支持,我們可以使用?switch?來重構代碼,使代碼更易于閱讀和擴展。

func Hello(name string, language string) string {
if name == "" {
name = "World"
}

prefix := helloPrefix

switch language {
case french:
prefix = frenchHelloPrefix
case spanish:
prefix = spanishHelloPrefix
}

return prefix + name
}

編寫一個測試,添加用你選擇的語言寫的問候,你應該可以看到擴展這個?神奇?的函數是多么簡單。

最后一次重構?

你可能會抱怨說也許我們的函數正在變得很臃腫。對此最簡單的重構是將一些功能提取到另一個函數中。

func Hello(name string, language string) string {
if name == "" {
name = "World"
}

return greetingPrefix(language) + name
}

func greetingPrefix(language string) (prefix string) {
switch language {
case french:
prefix = frenchHelloPrefix
case spanish:
prefix = spanishHelloPrefix
default:
prefix = englishPrefix
}
return
}

一些新的概念:

  • 在我們的函數簽名中,我們使用了?命名返回值(prefix string)。

  • 這將在你的函數中創建一個名為?prefix?的變量。

    • 你只需調用?return?而不是?return prefix?即可返回所設置的值。

    • 它將被分配「零」值。這取決于類型,例如?int?是 0,對于字符串它是?""

    • 這將顯示在 Go Doc 中,所以它使你的代碼更加清晰。

  • 如果沒有其他?case?語句匹配,將會執行?default?分支。

  • 函數名稱以小寫字母開頭。在 Go 中,公共函數以大寫字母開始,私有函數以小寫字母開頭。我們不希望我們算法的內部結構暴露給外部,所以我們將這個功能私有化。

總結

誰會知道你可以從?Hello, world?中學到這么多東西呢?

現在你應該以下內容有了一定的理解:

Go 的一些語法

  • 編寫測試

  • 用參數和返回類型聲明函數

  • ifelseswitch

  • 聲明變量和常量

TDD 過程以及步驟的重要性

  • 編寫一個失敗的測試,并查看失敗信息,可以看到我們已經為需求寫了一個?相關?的測試,并且看到它產生了一個?易于理解的失敗描述

  • 編寫最少量的代碼以使其通過,因此我們知道我們有可工作軟件

  • 然后?重構,支持我們測試的安全性,以確保我們擁有易于使用的精心制作的代碼

在我們的例子中,我們通過小巧易懂的步驟從?Hello()?到?Hello("name"),到?Hello("name", "french")

與「現實世界」的軟件相比,這當然是微不足道的,但原則依然通用。TDD 是一門需要通過開發去實踐的技能,通過將問題分解成更小的可測試的組件,你編寫軟件將會更加輕松。


作者:Chris James[5]譯者:Donng[6]校對:polaris1119[7],pityonline[8]

本文由?GCTT[9]?原創編譯,Go 中文網[10]?榮譽推出

推薦閱讀:

  • 通過測試學習Go:安裝 Go,搭建開發環境

  • 通過測試學習 Go 語言

喜歡該系列的朋友,歡迎關注“Go語言中文網”:

參考資料

[1]

你可以在這里查看本章的所有代碼:?https://github.com/quii/learn-go-with-tests/tree/master/hello-world

[2]

章節:?https://github.com/studygolang/learn-go-with-tests/blob/master/zh-CN/install-go.md#Go-環境

[3]

localhost:8000/pkg:?localhost:8000/pkg

[4]

http://localhost:8000/pkg/testing/:?http://localhost:8000/pkg/testing/

[5]

Chris James:?https://dev.to/quii

[6]

Donng:?https://github.com/Donng

[7]

polaris1119:?https://github.com/polaris1119

[8]

pityonline:?https://github.com/pityonline

[9]

GCTT:?https://github.com/studygolang/GCTT

[10]

Go 中文網:?https://studygolang.com/

總結

以上是生活随笔為你收集整理的go定时器 每天重复_通过测试学习Go:Hello, World的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。