Go單元測試編寫的五個建議

測試驅動開發是保持高代碼質量的好方法,同時保護本身免於迴歸,並向本身和其餘人證實本身的代碼完成了預期的工做。 這裏有五個技巧和竅門能夠改善你的測試。函數

把你的測試放在一個不一樣的包裏

Go堅持同一個文件夾中的文件屬於同一個包,除了_test.go文件。將測試代碼移出軟件包,可讓您編寫測試,就好像您是軟件包的真正用戶。你不能擺弄內部,而是專一於暴露的界面,並老是想着你可能會添加到你的API的任何噪音。

這樣可讓你自由地更改內部,而無需調整測試代碼。單元測試

在不一樣的文件中進行內部測試

若是你確實須要對一些內部進行單元測試,用_internal_test.go做爲後綴建立另外一個文件。內部測試必然比接口測試更脆弱, 可是它們是確保內部組件行爲的一個很好的方法,並且若是你使用測試驅動開發的話,它是特別有用的。測試

運行全部測試保存

去創建和運行速度很是快,因此沒有任何理由能夠說爲何不每次保存時都運行整個測試case。ui

當你在這個時候,爲何不去同時運行go vet,golint和goimports呢?

在Sublime Text中,這能夠經過安裝GoSublime並在添加如下配置項以前按下 Cmd +.,5來實現:code

「on_save」: [{
  「cmd」: 「gs9o_run_many」, 「args」: {
    「commands」:[
      [「clear」],
      [「sh」, 「if [ -f onsave.sh ]; then ./onsave.sh; else gofmt -s -w ./ && go build . errors && go test -i && go test && go vet && golint ; fi」]
    ],
    「focus_view」: false
  }
}],
「fmt_cmd」: [「goimports」]

上面的腳本首先檢查項目是否有一個onsave.sh文件,它會運行。這使您能夠輕鬆關閉不適用的軟件包的自動測試功能。對象

寫表驅動的測試

匿名結構和複合文字容許咱們寫出很是清晰和簡單的表格測試,而不依賴任何外部包。 下面的代碼容許咱們爲一個還沒有寫成的「Fib」函數設置一系列的測試:blog

var fibTests = [] struct { 
  n int // input 
  expected int //預期結果
} { 
  {1,1},
  {2,1},
  {3,2},
  {4,3},
  {5,5},
  {6,8},
  {7,13},
}

而後,咱們的測試函數只是覆蓋在切片上,在聲明結果正確以前爲每一個「n」調用「Fib」方法:接口

func TestFib(t *testing.T) {
  for _, tt := range fibTests {
    actual := Fib(tt.n)
    if actual != tt.expected {
      t.Errorf("Fib(%d): expected %d, actual %d", tt.n, tt.expected, actual)
    }
  }
}

看看你是否能夠本身寫「Fib」函數來讓測試經過,或者你能夠從Dave Chaney那裏找到解決方法。ip

使用Go代碼模擬接口

若是你須要模擬你的代碼依賴的東西來正確地測試它,那麼極可能是一個接口的好選擇。即便你依賴於一個你不能改變的外部包,你的代碼仍然可使用一個外部類型將會知足的接口。ci

通過幾年的寫mocks接口,我終於找到了模擬接口的完美方式,而不須要添加任何依賴項到咱們的項目中: [check out Moq](https://medium.com/@matryer/meet-moq-easily-mock-interfaces-in-go-476444187d10)。

假設咱們正在導入這個外部包:

package mailman
import 「net/mail」
type MailMan struct{}
func (m *MailMan) Send(subject, body string, to ...*mail.Address) {
  // some code
}
func New() *MailMan {
  return &MailMan{}
}

若是咱們正在測試的代碼須要一個MailMan對象,咱們的測試代碼能夠調用它的惟一方法是提供一個實際的MailMan實例。

func SendWelcomeEmail(m * MailMan,to ... * mail.Address){...}

這意味着每當咱們運行咱們的測試,一個真正的電子郵件能夠發送。想象一下,若是咱們已經實現了上面的保存功能。咱們很快就會惹惱咱們的測試用戶,或者拿出一大筆服務費用。 另外一種方法是將這個簡單的接口添加到您的代碼中:

type EmailSender interface{
  Send(subject, body string, to ...*mail.Address)
}

固然,MailMan已經知足了這個接口,由於咱們首先從他的Send方法簽名 - 因此咱們仍然能夠像之前同樣傳入MailMan對象。 但如今咱們能夠寫一個測試郵件發件人:

type testEmailSender struct{
  lastSubject string
  lastBody    string
  lastTo      []*mail.Address
}
// make sure it satisfies the interface
var _ package.EmailSender = (*testEmailSender)(nil) 
func (t *testEmailSender) Send(subject, body string, to ...*mail.Address) {
  t.lastSubject = subject
  t.lastBody = body
  t.lastTo = to
}

如今咱們能夠更新咱們的SendWelcomeEmail函數來取接口,而不是具體的類型:

func SendWelcomeEmail(m EmailSender,to ... * mail.Address){...}

在咱們的測試代碼中,咱們能夠發送咱們的假髮送者,並在調用目標函數以後在這些字段上進行斷言:

func TestSendWelcomeEmail(t *testing.T) {
  sender := &testEmailSender{}
  SendWelcomeEmail(sender, to1, to2)
  if sender.lastSubject != "Welcome" {
    t.Error("Subject line was wrong")
  }
  if sender.To[0] != to1 && sender.To[1] != to2 {
    t.Error("Wrong recipients")
  }
}

若是你想進一步探索mock特性,那麼必定要看看check out Moq, 它不只描述了一個寫模擬的好方法,並且還爲你寫。

相關文章
相關標籤/搜索