2 回答

TA貢獻(xiàn)1810條經(jīng)驗(yàn) 獲得超5個(gè)贊
您的代碼需要進(jìn)行一些重構(gòu)以使其可測(cè)試。目前您無(wú)法真正測(cè)試代碼,因?yàn)榇a中的函數(shù)不返回任何內(nèi)容。在單元測(cè)試中,您調(diào)用一個(gè)函數(shù)并驗(yàn)證它的輸出(一般來(lái)說(shuō))。
因此,為了使您的代碼可測(cè)試,您必須在單獨(dú)的函數(shù)中重構(gòu)代碼的某些部分。我將向您展示閏年的示例:
//main.go
func IsLeapYear(date time.Time) bool {
if date.Year() % 400 == 0 {
return true
}
return date.Year()%4 == 0 && date.Year()%100 != 0
}
在您的測(cè)試文件中:
//main_test.go
type dateTest struct {
date time.Time
expect bool
}
var dateTests = []dateTest{
// your test data
}
func TestIsLeapYear(t *testing.T){
for _, tc := range dateTests {
result := IsLeapYear(tc.data)
assert.Equal(t, tc.expect, result)
}
}

TA貢獻(xiàn)1825條經(jīng)驗(yàn) 獲得超6個(gè)贊
關(guān)于您更新的代碼:
我將從我給您的最重要的一條建議開(kāi)始: 停止忽略錯(cuò)誤!檢查錯(cuò)誤對(duì)于調(diào)試 Go 代碼和運(yùn)行程序絕對(duì)至關(guān)重要。請(qǐng)記住,與 Javascript 等許多高級(jí)語(yǔ)言不同,Go 沒(méi)有例外。您對(duì)錯(cuò)誤返回的檢查是您必須捕獲這些錯(cuò)誤的唯一機(jī)會(huì)。
例如:
json.Unmarshal(byteValue, &users)
如果 json 無(wú)效,該行將返回錯(cuò)誤。你應(yīng)該檢查一下。查找您使用的每個(gè)庫(kù)函數(shù)的文檔,但您沒(méi)有記住 - 我會(huì) - 并確保您正在使用這些錯(cuò)誤消息。
好的,讓我們看看你傳遞給什么IsLeapYear
:
jsonFile, err := os.Open("users.json") ... IsLeapYear("users.json")
當(dāng)你到達(dá)時(shí),IsLeapYear
你已經(jīng)過(guò)了需要文件名的地步。您已經(jīng)打開(kāi)了該文件并將其值解組為一個(gè)Users
結(jié)構(gòu)。因此,您當(dāng)然不需要將文件名傳遞給它。
讓我強(qiáng)調(diào)一下 Pim 向您展示的一些您沒(méi)有忠實(shí)遵循的內(nèi)容:
func IsLeapYear(date time.Time) bool
IsLeapYear
Pim檢查一個(gè)日期這一事實(shí)很重要。您的測(cè)試用例是需要單獨(dú)檢查的單獨(dú)日期。但是您 IsLeapYear
會(huì)遍歷整個(gè)日期列表。您無(wú)法提供要檢查的特定日期。您的閏年還希望收到多個(gè)日期,但您只返回一個(gè)bool
,因此單個(gè)返回值不可能告訴您閏年檢查您可能已經(jīng)過(guò)去的多個(gè)日期的結(jié)果。
按照 Pim 的建議IsLeapYear
- 它應(yīng)該只有一個(gè)參數(shù),并且應(yīng)該是time.Time
. 名字和姓氏不相關(guān),IsLeapYear
因此不需要傳遞。 IsLeapYear
不需要解析日期 - 它們的格式與您在 json 中存儲(chǔ)數(shù)據(jù)的方式有關(guān),并且與它們所代表的日期是否是閏年無(wú)關(guān)。
瞄準(zhǔn) 10-20 行功能。
我提出這個(gè)建議的原因是,長(zhǎng)函數(shù)幾乎總是做太多事情,這表明您需要重構(gòu)函數(shù)以更好地匹配算法的組件。讓我們將您的代碼分解為偽代碼以更好地反映您的算法:
從 json 文件中讀取用戶
對(duì)于每個(gè)用戶:
打印用戶信息
打印閏年消息
如果生日是今天,打印用戶信息和生日信息
否則打印不是生日信息
從預(yù)期格式解析其日期
如果是閏年生日
否則打印用戶信息而不是生日信息
從中我們可以確定我們可能編寫的一些函數(shù):
func ReadUsersFromJson(filename string) (*Users, err)
func ParseUserDateString(date string) (time.Time, error)
func IsLeapYear(date time.Time) bool
func PrintUserInfo(u User)
func IsTodaysDate(date time.Time) bool
這些功能中的每一個(gè)都非常簡(jiǎn)單明了,而且只做一件事。它們中的每一個(gè)都可以依次進(jìn)行測(cè)試。你的一般測(cè)試方法很常見(jiàn):寫一個(gè)完整的輸入列表(和預(yù)期的輸出- 你錯(cuò)過(guò)了那部分),然后遍歷列表,將每個(gè)輸入傳遞給正在測(cè)試的函數(shù),并驗(yàn)證它的結(jié)果是你的預(yù)期的。
因?yàn)?code>ReadUsersFromJson您可能有一個(gè)tests/
目錄,其中包含您測(cè)試的一些 json 文件。您可以使用有效和無(wú)效的 json 測(cè)試成功和錯(cuò)誤情況。
同樣,ParseUserDateString測(cè)試數(shù)據(jù)可能類似于:
struct ParseUserDateStringTestData {
input string
expected time.Time
exp_err_msg string
}
然后你可以在那里測(cè)試成功和失敗的案例。其余的功能依此類推。
一旦所有的功能都被編寫和測(cè)試了,這只是一個(gè)將所有功能組裝在一起的問(wèn)題Birthday()。
func Birthday() {
users, err := ReadUsersFromJson("users.json")
if err != nil {
panic(fmt.Errorf("Failed to read from Json: %w", err))
}
for _, user := range users.Users {
if d, err := ParseUserDateString(user.Date); err != nil {
panic(fmt.Errorf("Date %s could not be parsed: %w", user.Date, err))
} else if IsLeapYear(d) {
PrintUserInfo(user)
fmt.Printf("Leap year message!")
if IsTodaysDate(d) {
fmt.Printf("birthday message!")
} else {
fmt.Printf("Not birthday message!")
}
} else {
fmt.Printf("Not leap year message!")
}
}
}
它恰好出現(xiàn)在 20 行,而且它在做什么也很清楚。您會(huì)注意到,自從我編寫它以來(lái),PrintUserInfo它刪除了Birthday. 當(dāng)然,這種重復(fù)使函數(shù)更難閱讀和管理。
如果您編寫像我列出的那樣的函數(shù),并測(cè)試所有這些函數(shù),那么您的程序應(yīng)該可以很好地結(jié)合在一起。請(qǐng)注意,main()或Birthday()不需要編寫來(lái)測(cè)試其他功能。在編寫函數(shù)時(shí)測(cè)試它們是一個(gè)好主意,以免最終積壓大量測(cè)試,并灌輸對(duì)您編寫的代碼的信心。
一個(gè)例子:
if date.Day() == time.Now().Day() {
我不認(rèn)為那正在做你認(rèn)為它正在做的事情。 Day()實(shí)際上是月份的日期,所以你實(shí)際上只是說(shuō),日期的日期與今天的日期是同一天嗎?任何一個(gè)月Day()的 29 日都與 2 月 29 日相同。測(cè)試將證明是否是這種情況,然后您可以稍后依賴該功能。同樣,ParseUserDateString測(cè)試數(shù)據(jù)可能類似于:
struct ParseUserDateStringTestData {
input string
expected time.Time
exp_err_msg string
}
然后你可以在那里測(cè)試成功和失敗的案例。其余的功能依此類推。
一旦所有的功能都被編寫和測(cè)試了,這只是一個(gè)將所有功能組裝在一起的問(wèn)題Birthday()。
func Birthday() {
users, err := ReadUsersFromJson("users.json")
if err != nil {
panic(fmt.Errorf("Failed to read from Json: %w", err))
}
for _, user := range users.Users {
if d, err := ParseUserDateString(user.Date); err != nil {
panic(fmt.Errorf("Date %s could not be parsed: %w", user.Date, err))
} else if IsLeapYear(d) {
PrintUserInfo(user)
fmt.Printf("Leap year message!")
if IsTodaysDate(d) {
fmt.Printf("birthday message!")
} else {
fmt.Printf("Not birthday message!")
}
} else {
fmt.Printf("Not leap year message!")
}
}
}
它恰好出現(xiàn)在 20 行,而且它在做什么也很清楚。您會(huì)注意到,自從我編寫它以來(lái),PrintUserInfo它刪除了Birthday. 當(dāng)然,這種重復(fù)使函數(shù)更難閱讀和管理。
如果您編寫像我列出的那樣的函數(shù),并測(cè)試所有這些函數(shù),那么您的程序應(yīng)該可以很好地結(jié)合在一起。請(qǐng)注意,main()或Birthday()不需要編寫來(lái)測(cè)試其他功能。在編寫函數(shù)時(shí)測(cè)試它們是一個(gè)好主意,以免最終積壓大量測(cè)試,并灌輸對(duì)您編寫的代碼的信心。
一個(gè)例子:
if date.Day() == time.Now().Day() {
我不認(rèn)為那正在做你認(rèn)為它正在做的事情。 Day()實(shí)際上是月份的日期,所以你實(shí)際上只是說(shuō),日期的日期與今天的日期是同一天嗎?任何一個(gè)月Day()的 29 日都與 2 月 29 日相同。測(cè)試將證明是否是這種情況,然后您可以稍后依賴該功能。
- 2 回答
- 0 關(guān)注
- 134 瀏覽
添加回答
舉報(bào)