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

Golang 测试中的夹具

Golang 测试中的夹具

Go
白衣非少年 2021-12-07 14:42:34
来自 python 世界,fixtures 非常有用(Fixtures 为可重用状态/支持逻辑定义了一个 Python 契约,主要用于单元测试)。我想知道 Golang 是否有类似的支持,它可以让我使用一些预定义的装置来运行我的测试,比如设置服务器,拆除它,每次运行测试时都做一些重复的任务?有人可以指出我在 Golang 中做同样事情的一些例子吗?
查看完整描述

3 回答

?
繁星coding

TA贡献1797条经验 获得超4个赞

如果你想使用标准的 Go 测试工具,你可以定义一个带有签名的函数TestMain(m *testing.M)并将你的夹具代码放在那里。


从测试包维基:


有时,测试程序需要在测试之前或之后进行额外的设置或拆卸。有时也需要测试来控制哪些代码在主线程上运行。为了支持这些和其他情况,如果测试文件包含一个函数:


func TestMain(m *testing.M)


那么生成的测试将调用 TestMain(m) 而不是直接运行测试。TestMain 运行在主 goroutine 中,并且可以围绕对 m.Run 的调用进行任何必要的设置和拆卸。然后它应该使用 m.Run 的结果调用 os.Exit。调用 TestMain 时,flag.Parse 尚未运行。如果 TestMain 依赖于命令行标志,包括测试包的那些,它应该显式调用 flag.Parse。


TestMain 的一个简单实现是:


func TestMain(m *testing.M) {

    flag.Parse()

    os.Exit(m.Run())

}


查看完整回答
反对 回复 2021-12-07
?
一只名叫tom的猫

TA贡献1906条经验 获得超3个赞

我知道这是一个老问题,但这仍然出现在搜索结果中,所以我想我会给出一个可能的答案。


您可以将代码隔离到辅助函数中,这些辅助函数返回一个“拆卸”函数以在其自身之后进行清理。这是启动服务器并在测试用例结束时关闭它的一种可能方法。


func setUpServer() (string, func()) {

    h := func(w http.ResponseWriter, r *http.Request) {

        code := http.StatusTeapot

        http.Error(w, http.StatusText(code), code)

    }


    ts := httptest.NewServer(http.HandlerFunc(h))

    return ts.URL, ts.Close

}


func TestWithServer(t *testing.T) {

    u, close := setUpServer()

    defer close()


    rsp, err := http.Get(u)

    assert.Nil(t, err)

    assert.Equal(t, http.StatusTeapot, rsp.StatusCode)

}

这将启动一个服务器net/http/httptest并返回它的 URL 以及一个充当“拆卸”的函数。这个函数被添加到 defer 堆栈中,因此无论测试用例如何退出,它总是被调用。


或者,*testing.T如果您有更复杂的设置并且需要处理错误,您可以传入。此示例显示设置函数返回一个*url.URL而不是 URL 格式的字符串,并且解析可能会返回一个错误。


func setUpServer(t *testing.T) (*url.URL, func()) {

    h := func(w http.ResponseWriter, r *http.Request) {

        code := http.StatusTeapot

        http.Error(w, http.StatusText(code), code)

    }


    ts := httptest.NewServer(http.HandlerFunc(h))

    u, err := url.Parse(ts.URL)

    assert.Nil(t, err)

    return u, ts.Close

}


func TestWithServer(t *testing.T) {

    u, close := setUpServer(t)

    defer close()


    u.Path = "/a/b/c/d"

    rsp, err := http.Get(u.String())

    assert.Nil(t, err)

    assert.Equal(t, http.StatusTeapot, rsp.StatusCode)

}


查看完整回答
反对 回复 2021-12-07
?
沧海一幻觉

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

我写了golang引擎来使用类似于pytest的fixtures:https : //github.com/rekby/fixenv


用法示例:


package example


// db create database abd db struct, cached per package - call

// once and same db shared with all tests

func db(e Env)*DB{...}


// DbCustomer - create customer with random personal data

// but fixed name. Fixture result shared by test and subtests, 

// then mean many calls Customer with same name will return same

// customer object.

// Call Customer with other name will create new customer

// and resurn other object.

func DbCustomer(e Env, name string) Customer {

    // ... create customer

    db(e).CustomerStore(cust)

    // ...

    return cust

}


// DbAccount create bank account for customer with given name.

func DbAccount(e Env, customerName, accountName string)Account{

    cust := DbCustomer(e, customerName)

    // ... create account

    db(e).AccountStore(acc)

    // ...

    return acc

}


func TestFirstOwnAccounts(t *testing.T){

    e := NewEnv(t)

    // background:

    // create database

    // create customer bob 

    // create account from

    accFrom := DbAccount(e, "bob", "from")

    

    // get existed db, get existed bob, create account to

    accTo := DbAccount(e, "bob", "to")

    

    PutMoney(accFrom, 100)

    SendMoney(accFrom, accTo, 20)

    if accFrom != 80 {

        t.Error()

    }

    if accTo != 20 {

        t.Error()   

    }

    

    // background:

    // delete account to

    // delete account from

    // delete customer bob

}


func TestSecondTransferBetweenCustomers(t *testing.T){

    e := NewEnv(t)

    

    // background:

    // get db, existed from prev test

    // create customer bob

    // create account main for bob

    accFrom := DbAccount(e, "bob", "main")

    

    // background:

    // get existed db

    // create customer alice

    // create account main for alice

    accTo := DbAccount(e, "alice", "main")

    PutMoney(accFrom, 100)

    SendMoney(accFrom, accTo, 20)

    if accFrom != 80 {

        t.Error()

    }

    if accTo != 20 {

        t.Error()

    }

    

    // background:

    // remove account of alice

    // remove customer alice

    // remove account of bob

    // remove customer bob

}


// background:

// after all test finished drop database


查看完整回答
反对 回复 2021-12-07
  • 3 回答
  • 0 关注
  • 151 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信