手機(jī)網(wǎng)站有哪些類(lèi)型成都網(wǎng)絡(luò)推廣
快速開(kāi)始
GoConvey是一個(gè)完全兼容官方Go Test的測(cè)試框架,一般來(lái)說(shuō)這種第三方庫(kù)都比官方的功能要強(qiáng)大、更加易于使用、開(kāi)發(fā)效率更高,閑話少說(shuō),先看一個(gè)example:
package utils
import (. "github.com/smartystreets/goconvey/convey""testing"
)func TestSpec(t *testing.T) {Convey("Given some integer with a starting value", t, func() {x := 1Convey("When the integer is incremented", func() {x++Convey("The value should be greater by one", func() {So(x, ShouldEqual, 2)})})})
}
看著復(fù)雜, 一層層的嵌套,如果你使用IDE的話你可以點(diǎn)到源碼里面看一下其方法注釋,其實(shí)已經(jīng)說(shuō)的非常清楚了,這里摘取部分看一下:
// Convey is the method intended for use when declaring the scopes of
// a specification. Each scope has a description and a func() which may contain
// other calls to Convey(), Reset() or Should-style assertions. Convey calls can
// be nested as far as you see fit.
//
// IMPORTANT NOTE: The top-level Convey() within a Test method
// must conform to the following signature:
//
// Convey(description string, t *testing.T, action func())
//
// All other calls should look like this (no need to pass in *testing.T):
//
// Convey(description string, action func())
這個(gè)用法相對(duì)簡(jiǎn)單了,Convey定義了一個(gè)局部的作用域,在這個(gè)作用域里面我們可以定義變量,調(diào)用方法,然后重復(fù)繼續(xù)這個(gè)操作,low-level的Convey會(huì)繼承top-level的變量。
了解之后,我們來(lái)擴(kuò)展一下這個(gè)例子:
func TestSpec(t *testing.T) {Convey("Given some integer with a starting value", t, func() {x := 1y := 10Convey("When the integer is incremented", func() {x++Convey("The value should be greater by one", func() {So(x, ShouldEqual, 2)})})Convey("When x < y", func() {if x < y {x = x + ySo(x, ShouldBeGreaterThan, y)}})})
}
非常簡(jiǎn)單,當(dāng)然這里我們并沒(méi)有測(cè)試任何函數(shù)或方法,下面咱們寫(xiě)一個(gè)函數(shù)真正測(cè)試一下,假設(shè)有下面的方法:
func Div(a, b int) (int, error) {if b == 0 {return 0, errors.New("can not div zero")}return a / b, nil
}
使用GoConvey的話,測(cè)試代碼可以這么寫(xiě):
func TestDiv(t *testing.T) {const X = 10Convey("Normal Result", t, func() {res, err := Div(X, 2)So(res, ShouldEqual, 5)So(err, ShouldBeNil)Convey("Extend Scope", func() {res, err := Div(res, 2)So(res, ShouldEqual, 2)So(err, ShouldBeNil)})})Convey("Error Result", t, func() {res, err := Div(X, 0)So(res, ShouldEqual, 0)So(err, ShouldNotBeNil)})
}
有人可能會(huì)覺(jué)得這和官方的沒(méi)多大區(qū)別,相當(dāng)于多加了一個(gè)注釋,可以對(duì)每一個(gè)測(cè)試用例標(biāo)識(shí),但是不僅僅如此,這個(gè)庫(kù)還提供了大量增強(qiáng)的Assertions,可以非常方便的對(duì)字符串、slice、map結(jié)果進(jìn)行斷言測(cè)試,具體的話可以查看一下文檔或者點(diǎn)進(jìn)去看看源碼注釋,這些源碼注釋基本上已經(jīng)寫(xiě)的非常清楚了。
Web UI
此外,框架還提供了一個(gè)Web端的UI界面,可以非常方便的查看測(cè)試覆蓋和運(yùn)行情況,還可以自動(dòng)運(yùn)行測(cè)試,執(zhí)行g(shù)oconvey命令就可以啟動(dòng)服務(wù),快試一試吧!(雖然說(shuō)像Goland這樣的IDE也提供了GUI工具查看測(cè)試覆蓋率,但是這個(gè)更加方便)
另外,這個(gè)框架還提供了自定義Assertions的功能,使用起來(lái)也很方便,有一個(gè)通用的模板:
func should<do-something>(actual interface{}, expected ...interface{}) string {if <some-important-condition-is-met(actual, expected)> {return "" // empty string means the assertion passed}return "<some descriptive message detailing why the assertion failed...>"
}
舉個(gè)例子,這里定義一個(gè)試試:
func shouldNotGreatThan100(actual interface{}, expected ...interface{}) string {if actual.(int) > 100 {return "too big than 100"} else {return ""}
}
定義通用的邏輯
有時(shí)候測(cè)試會(huì)需要做一些準(zhǔn)備工作,而且是重復(fù)的,比如說(shuō)一些初始化操作,這時(shí)候就可以定義一個(gè)函數(shù)完成這件事,不必每次測(cè)試重復(fù)做,官方文檔里面舉了一個(gè)數(shù)據(jù)庫(kù)測(cè)試的例子,每次測(cè)試前開(kāi)啟事務(wù),測(cè)試結(jié)束后回滾事務(wù),這里貼一下官方的example,大家看一下,很容易理解:
package main
import ("database/sql""testing"_ "github.com/lib/pq". "github.com/smartystreets/goconvey/convey"
)
func WithTransaction(db *sql.DB, f func(tx *sql.Tx)) func() {return func() {tx, err := db.Begin()So(err, ShouldBeNil)Reset(func() {/* Verify that the transaction is alive by executing a command */_, err := tx.Exec("SELECT 1")So(err, ShouldBeNil)tx.Rollback()})/* Here we invoke the actual test-closure and provide the transaction */f(tx)}
}
func TestUsers(t *testing.T) {db, err := sql.Open("postgres", "postgres://localhost?sslmode=disable")if err != nil {panic(err)}Convey("Given a user in the database", t, WithTransaction(db, func(tx *sql.Tx) {_, err := tx.Exec(`INSERT INTO "Users" ("id", "name") VALUES (1, 'Test User')`)So(err, ShouldBeNil)Convey("Attempting to retrieve the user should return the user", func() {var name stringdata := tx.QueryRow(`SELECT "name" FROM "Users" WHERE "id" = 1`)err = data.Scan(&name)So(err, ShouldBeNil)So(name, ShouldEqual, "Test User")})}))
}
/* Required table to run the test:
CREATE TABLE "public"."Users" ( "id" INTEGER NOT NULL UNIQUE, "name" CHARACTER VARYING( 2044 ) NOT NULL
);
*/