为了账号安全,请及时绑定邮箱和手机立即绑定

我如何应用单元测试来检查 golang 的闰年?

我如何应用单元测试来检查 golang 的闰年?

Go
江户川乱折腾 2022-10-10 15:38:41
我想做单元测试并检查日期是否是闰年,在这种情况下我如何应用单元测试,我不知道在这种情况下该怎么做?我已经创建了我的函数并且工作正常,我还有一个 JSON 文件,其中包含人员列表及其生日日期!希望能提供一些有关如何解决此问题的反馈或建议!谢谢BD.go: package mainimport (    "encoding/json"    "fmt"    "io/ioutil"    "log"    "os"    "time")// Users struct which contains// an array of userstype Users struct {    Users []User `json:"users"`}// User struct which contains a name// a type and a list of social linkstype User struct {    Firstname  string `json:"fname"`    Secondname string `json:"lname"`    Date       string `json:"date"`}var users Usersvar user Userfunc Birthday() {    // Open our jsonFile    jsonFile, err := os.Open("users.json")    // if we os.Open returns an error then handle it    if err != nil {        fmt.Println(err)    }    fmt.Println("Successfully Opened users.json")    // defer the closing of our jsonFile so that we can parse it later on    defer jsonFile.Close()    // read our opened xmlFile as a byte array.    byteValue, _ := ioutil.ReadAll(jsonFile)    // we initialize our Users array    // we unmarshal our byteArray which contains our    // jsonFile's content into 'users' which we defined above    json.Unmarshal(byteValue, &users)    IsLeapYear("users.json")}func IsLeapYear(user string) bool {    // we iterate through every user within our users array and    // print out the user Type, their name    for i := 0; i < len(users.Users); i++ {        date, err := time.Parse("2006/01/02", users.Users[i].Date)        if err != nil {            date, err = time.Parse("2006-01-02", users.Users[i].Date)            // date, err = time.Parse("2006 01 02", users.Users[i].Date)            if err != nil {                log.Fatal("unsupported date format:", err)            }        }
查看完整描述

2 回答

?
森栏

TA贡献1810条经验 获得超5个赞

您的代码需要进行一些重构以使其可测试。目前您无法真正测试代码,因为代码中的函数不返回任何内容。在单元测试中,您调用一个函数并验证它的输出(一般来说)。


因此,为了使您的代码可测试,您必须在单独的函数中重构代码的某些部分。我将向您展示闰年的示例:


//main.go

func IsLeapYear(date time.Time) bool {

  if date.Year() % 400 == 0 {

    return true

  }

  return date.Year()%4 == 0 && date.Year()%100 != 0

}

在您的测试文件中:


//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)

  }

}


查看完整回答
反对 回复 2022-10-10
?
胡子哥哥

TA贡献1825条经验 获得超6个赞

关于您更新的代码:

我将从我给您的最重要的一条建议开始: 停止忽略错误!检查错误对于调试 Go 代码和运行程序绝对至关重要。请记住,与 Javascript 等许多高级语言不同,Go 没有例外。您对错误返回的检查是您必须捕获这些错误的唯一机会。

例如:

    json.Unmarshal(byteValue, &users)

如果 json 无效,该行将返回错误。你应该检查一下。查找您使用的每个库函数的文档,但您没有记住 - 我会 - 并确保您正在使用这些错误消息。

好的,让我们看看你传递给什么IsLeapYear

    jsonFile, err := os.Open("users.json")
    ...
    IsLeapYear("users.json")

当你到达时,IsLeapYear你已经过了需要文件名的地步。您已经打开了该文件并将其值解组为一个Users结构。因此,您当然不需要将文件名传递给它。

让我强调一下 Pim 向您展示的一些您没有忠实遵循的内容:

func IsLeapYear(date time.Time) bool

IsLeapYearPim检查一个日期这一事实很重要。您的测试用例是需要单独检查的单独日期。但是 IsLeapYear会遍历整个日期列表。您无法提供要检查的特定日期。您的闰年还希望收到多个日期,但您只返回一个bool,因此单个返回值不可能告诉您闰年检查您可能已经过去的多个日期的结果。

按照 Pim 的建议IsLeapYear- 它应该只有一个参数,并且应该是time.Time. 名字和姓氏不相关,IsLeapYear因此不需要传递。 IsLeapYear不需要解析日期 - 它们的格式与您在 json 中存储数据的方式有关,并且与它们所代表的日期是否是闰年无关。

瞄准 10-20 行功能。

我提出这个建议的原因是,长函数几乎总是做太多事情,这表明您需要重构函数以更好地匹配算法的组件。让我们将您的代码分解为伪代码以更好地反映您的算法:

  • 从 json 文件中读取用户

  • 对于每个用户:

    • 打印用户信息

    • 打印闰年消息

    • 如果生日是今天,打印用户信息和生日信息

    • 否则打印不是生日信息

    • 从预期格式解析其日期

    • 如果是闰年生日

    • 否则打印用户信息而不是生日信息

从中我们可以确定我们可能编写的一些函数:

  • 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

这些功能中的每一个都非常简单明了,而且只做一件事。它们中的每一个都可以依次进行测试。你的一般测试方法很常见:写一个完整的输入列表(和预期的输出- 你错过了那部分),然后遍历列表,将每个输入传递给正在测试的函数,并验证它的结果是你的预期的。

因为ReadUsersFromJson您可能有一个tests/目录,其中包含您测试的一些 json 文件。您可以使用有效和无效的 json 测试成功和错误情况。

同样,ParseUserDateString测试数据可能类似于:


struct ParseUserDateStringTestData {

  input string

  expected time.Time

  exp_err_msg string

}

然后你可以在那里测试成功和失败的案例。其余的功能依此类推。


一旦所有的功能都被编写和测试了,这只是一个将所有功能组装在一起的问题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!")

      }

   }

}

它恰好出现在 20 行,而且它在做什么也很清楚。您会注意到,自从我编写它以来,PrintUserInfo它删除了Birthday. 当然,这种重复使函数更难阅读和管理。


如果您编写像我列出的那样的函数,并测试所有这些函数,那么您的程序应该可以很好地结合在一起。请注意,main()或Birthday()不需要编写来测试其他功能。在编写函数时测试它们是一个好主意,以免最终积压大量测试,并灌输对您编写的代码的信心。


一个例子:


            if date.Day() == time.Now().Day() {

我不认为那正在做你认为它正在做的事情。 Day()实际上是月份的日期,所以你实际上只是说,日期的日期与今天的日期是同一天吗?任何一个月Day()的 29 日都与 2 月 29 日相同。测试将证明是否是这种情况,然后您可以稍后依赖该功能。同样,ParseUserDateString测试数据可能类似于:


struct ParseUserDateStringTestData {

  input string

  expected time.Time

  exp_err_msg string

}

然后你可以在那里测试成功和失败的案例。其余的功能依此类推。


一旦所有的功能都被编写和测试了,这只是一个将所有功能组装在一起的问题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!")

      }

   }

}

它恰好出现在 20 行,而且它在做什么也很清楚。您会注意到,自从我编写它以来,PrintUserInfo它删除了Birthday. 当然,这种重复使函数更难阅读和管理。


如果您编写像我列出的那样的函数,并测试所有这些函数,那么您的程序应该可以很好地结合在一起。请注意,main()或Birthday()不需要编写来测试其他功能。在编写函数时测试它们是一个好主意,以免最终积压大量测试,并灌输对您编写的代码的信心。


一个例子:


            if date.Day() == time.Now().Day() {

我不认为那正在做你认为它正在做的事情。 Day()实际上是月份的日期,所以你实际上只是说,日期的日期与今天的日期是同一天吗?任何一个月Day()的 29 日都与 2 月 29 日相同。测试将证明是否是这种情况,然后您可以稍后依赖该功能。


查看完整回答
反对 回复 2022-10-10
  • 2 回答
  • 0 关注
  • 189 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号