[去]如何与测试中的日期一起工作
#go #tests #time #unit

与任何编程语言中的日期合作提出了一些挑战。在这篇文章中,我将展示如何在为GO应用程序编写单元测试时使用日期。

让我们去示例:

import (
    "time"

    "github.com/google/uuid"
)

type Food struct {
    ID             uuid.UUID
    Name           string
    ExpirationDate time.Time
}

func canIEat(f Food) bool {
    if time.Now().Before(f.ExpirationDate) {
        return true
    }
    return false
}

为此代码编写单元测试的一种方法可能是:

import (
    "testing"
    "time"

    "github.com/stretchr/testify/assert"
)

func TestCanIEat(t *testing.T) {
    f1 := Food{
        ExpirationDate: time.Now().AddDate(0, 0, 1),
    }
    assert.True(t, canIEat(f1))

    f2 := Food{
        ExpirationDate: time.Now().AddDate(0, 0, -1),
    }
    assert.False(t, canIEat(f2))
}

解决此问题的另一种方法是为time软件包创建抽象。为此,我们将创建一个名为clock的新软件包,其中将添加clock.go文件:

package clock

import "time"

// Clock interface
type Clock interface {
    Now() time.Time
}

// RealClock clock
type RealClock struct{}

// NewRealClock create a new real clock
func NewRealClock() *RealClock {
    return &RealClock{}
}

// Now returns the current data
func (c *RealClock) Now() time.Time {
    return time.Now()
}

下一步是重构将使用新软件包的功能:

import (
    "time"

    "github.com/eminetto/post-time/clock"
    "github.com/google/uuid"
)

type Food struct {
    ID             uuid.UUID
    Name           string
    ExpirationDate time.Time
}

func canIEat(c clock.Clock, f Food) bool {
    if c.Now().Before(f.ExpirationDate) {
        return true
    }
    return false
}

canIEat函数接收clock.Clock接口时,我们可以在测试中使用此接口的新实现:

import (
    "testing"
    "time"

    "github.com/stretchr/testify/assert"
)

type FakeClock struct{}

func (c FakeClock) Now() time.Time {
    return time.Date(2023, 6, 30, 20, 0, 0, 0, time.Local)
}

func TestCanIEat(t *testing.T) {
    type test struct {
        food     Food
        expected bool
    }
    fc := FakeClock{}
    cases := []test{
        {
            food: Food{
                ExpirationDate: time.Date(2020, 10, 1, 20, 0, 0, 0, time.Local),
            },
            expected: false,
        },
        {
            food: Food{
                ExpirationDate: time.Date(2023, 6, 30, 20, 0, 0, 0, time.Local),
            },
            expected: false,
        },
        {
            food: Food{
                ExpirationDate: time.Date(2023, 6, 30, 21, 0, 0, 0, time.Local),
            },
            expected: true,
        },
    }
    for _, c := range cases {
        assert.Equal(t, c.expected, canIEat(fc, c.food))
    }
}

以这种方式,我们更好地控制了我们在测试和增益性能中使用的方法,因为不再需要像第一个示例的time.Now().AddDate(0, 0, 1)进行日期计算。

我在这里做的是一个简单的提示,但它显示了使用interfaces in Go的概念的功能和易于使用。