Go'daki test paketini kullanarak test kurulumunu nasıl yapabilirim


Yanıtlar:


159

Go 1.4'ten başlayarak, kurulum / sökme uygulayabilirsiniz (işlevlerinizi her testten önce / sonra kopyalamanıza gerek yoktur). Dokümantasyon özetlenmiştir burada yer Ana bölümde:

TestMain, ana programda çalışır ve bir m.Run çağrısı etrafında gerekli kurulum ve sökme işlemlerini yapabilir. Daha sonra m.Run sonucuyla os.Exit'i çağırmalıdır.

Bunu anlamam biraz zaman aldı, bu, eğer bir test bir fonksiyon içeriyorsa func TestMain(m *testing.M), testi çalıştırmak yerine bu fonksiyonun çağrılacağı anlamına geliyor . Ve bu işlevde testlerin nasıl çalışacağını tanımlayabilirim. Örneğin, genel kurulum ve sökme uygulayabilirim:

func TestMain(m *testing.M) {
    setup()
    code := m.Run() 
    shutdown()
    os.Exit(code)
}

Birkaç başka örnek burada bulunabilir .

En son sürümde Go'nun test çerçevesine eklenen TestMain özelliği, birkaç test kullanım durumu için basit bir çözümdür. TestMain, kurulum ve kapatma gerçekleştirmek, test ortamını kontrol etmek, bir alt süreçte farklı kod çalıştırmak veya test koduyla sızan kaynakları kontrol etmek için genel bir kanca sağlar. Çoğu paket bir TestMain'e ihtiyaç duymaz, ancak gerekli olduğu zamanlar için hoş bir eklentidir.


17
TestMainbir pakette bir kez, bu yüzden o kadar kullanışlı değil. Daha karmaşık amaçlar için alt testleri daha iyi buluyorum .
İnanç Gümüş

3
Genel değişkenler kullanmadan bağlamı kurulum işlevinden testlere nasıl geçireceksiniz? Örneğin, mySetupFunction (), içinde test yapmak için (benzersiz, rastgele bir adla) geçici bir dizin oluşturursa, testler dizinin adını nasıl bilir? Bu bağlamı ayarlayacak bir yer olmalı ??
Lqueryvg

1
Görünüşe göre bu, testler için kancalardan önce ve sonra halletmenin resmi yolu, resmi belgeler için golang.org/pkg/testing/#hdr-Main'e bakın
de-jcup

4
@InancGumuslstat $GOROOT/subtests: no such file or directory
030

1
lütfen unutmayın 'code: = m.Run ()' diğer TestFunctions'ı çalıştırır!
Alex Punnen

49

Bu init(), _test.godosyaya bir işlev koyarak başarılabilir . Bu, init()işlevden önce çalıştırılacaktır .

// package_test.go
package main

func init() {
     /* load test data */
}

_Test.init (), paket init () işlevinden önce çağrılacaktır.


2
Kendi sorunuzu yanıtladığınızı biliyorum, bu nedenle bu muhtemelen kendi kullanım durumunuzu tatmin eder, ancak bu, sorunuza dahil ettiğiniz NUnit örneğiyle eşdeğer değildir.
James Henstridge

Pekala @james, soruyu yanıtlamak için bir fikir gösterdim ve diğerleri zaten sizinki de dahil olmak üzere bazı iyi içgörüler sağladı. Yaklaşımı ayarlamak için dış etkiler elde etmek faydalıdır. Teşekkürler.
miltonb

2
Yeterince adil. Bu cevapta gösterdiğiniz şey, [TestFixtureSetUp]bunun yerine NUnit'in özniteliğini kullanmaya biraz daha yakındır .
James Henstridge

2
yırtılma kısmı dahil değil
Taras Matsyk

7
Test dosyanız ana işlevle aynı pakette ise bu iyi bir çözüm değildir.
MouseWanted

28

Birim testi için basit bir işlev verildiğinde:

package math

func Sum(a, b int) int {
    return a + b
}

Teardown işlevini döndüren bir kurulum işleviyle test edebilirsiniz. Setup () çağrıldıktan sonra, ertelenmiş bir teardown () çağrısı yapabilirsiniz.

package math

import "testing"

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func setupSubTest(t *testing.T) func(t *testing.T) {
    t.Log("setup sub test")
    return func(t *testing.T) {
        t.Log("teardown sub test")
    }
}

func TestAddition(t *testing.T) {
    cases := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"add", 2, 2, 4},
        {"minus", 0, -2, -2},
        {"zero", 0, 0, 0},
    }

    teardownTestCase := setupTestCase(t)
    defer teardownTestCase(t)

    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            teardownSubTest := setupSubTest(t)
            defer teardownSubTest(t)

            result := Sum(tc.a, tc.b)
            if result != tc.expected {
                t.Fatalf("expected sum %v, but got %v", tc.expected, result)
            }
        })
    }
}

Go test aracı, kabuk konsolundaki günlükleme ifadelerini bildirir:

% go test -v
=== RUN   TestAddition
=== RUN   TestAddition/add
=== RUN   TestAddition/minus
=== RUN   TestAddition/zero
--- PASS: TestAddition (0.00s)
    math_test.go:6: setup test case
    --- PASS: TestAddition/add (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/minus (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/zero (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    math_test.go:8: teardown test case
PASS
ok      github.com/kare/go-unit-test-setup-teardown 0.010s
% 

Bu yaklaşımla kurulum / sökme için bazı ek parametreler iletebilirsiniz.


2
Şimdi bu gerçekten basit ama etkili bir numara. Go sözdiziminin harika kullanımı.
miltonb

1
Evet, ama iç içe geçmişliği artırır ( javascript'te bir tür kıyamet piramidi ). Ve testler, dış testlerde olduğu gibi paket tarafından otomatik olarak çalıştırılmaz.
İnanç Gümüş

12

Genellikle, go'daki testler diğer dillerle aynı tarzda yazılmaz. Genellikle, nispeten daha az test işlevi vardır, ancak her biri tabloya dayalı bir test senaryosu seti içerir. Go ekibinden biri tarafından yazılan bu makaleye bakın .

Tablo temelli bir testle, herhangi bir kurulum kodunu tabloda belirtilen ayrı test olaylarını yürüten döngüden önce koyarsınız ve daha sonra herhangi bir temizleme kodunu koyarsınız.

Test işlevleri arasında hala paylaşılan kurulum kodunuz varsa, paylaşılan kurulum kodunu bir işleve çıkarabilir ve sync.Oncetam olarak bir kez yürütülmesi önemliyse a kullanabilirsiniz (veya başka bir yanıtın önerdiği gibi kullanın init(), ancak bu, kurulumun dezavantajına sahiptir. test senaryoları çalıştırılmasa bile yapılacaktır (belki de kullanarak test senaryolarını sınırlandırdığınız için go test -run <regexp>).

Tam olarak bir kez yürütülen farklı testler arasında paylaşılan bir kuruluma ihtiyacınız olduğunu düşünüyorsanız, gerçekten ihtiyacınız olup olmadığını ve masa tabanlı bir testin daha iyi olmayacağını düşünmeniz gerektiğini söyleyebilirim.


7
Bu, bir bayrak ayrıştırıcı veya sayıları karıştıran bir algoritma gibi önemsiz şeyleri test ederken harika. Ancak, hepsi benzer standart kod gerektiren çeşitli işlevsellik parçalarını test etmeye çalışırken gerçekten yardımcı olmuyor. Sanırım test işlevlerimi bir dizide tanımlayabilir ve bunların üzerinde yineleyebilirim, ancak o zaman gerçekten tabloya dayalı değil, gerçekten yalnızca test çerçevesinin içine yerleştirilmesi gereken basit bir döngü (uygun bir test paketi biçiminde) kurulum /
sökme

9

Go test çerçevesi, NUnit'in SetUp özniteliğine eşdeğer herhangi bir şeye sahip değildir (paketteki her testten önce çağrılacak bir işlevi işaretler). Yine de birkaç seçenek var:

  1. SetUpGerekli olduğu her testten işlevinizi aramanız yeterlidir .

  2. Go'nun xUnit paradigmalarını ve kavramlarını uygulayan test çerçevesi için bir uzantı kullanın. Akla üç güçlü seçenek geliyor:

Bu kitaplıkların her biri, testlerinizi diğer xUnit çerçevelerine benzer paketler / fikstürler halinde düzenlemenizi teşvik eder ve her bir yöntemden önce paket / fikstür türündeki kurulum yöntemlerini çağırır Test*.


0

Utanmaz fiş, tam olarak bu sorunu çözmeye yardımcı olmak için https://github.com/houqp/gtest oluşturdum .

İşte hızlı bir örnek:

import (
  "strings"
  "testing"
  "github.com/houqp/gtest"
)

type SampleTests struct{}

// Setup and Teardown are invoked per test group run
func (s *SampleTests) Setup(t *testing.T)      {}
func (s *SampleTests) Teardown(t *testing.T)   {}
// BeforeEach and AfterEach are invoked per test run
func (s *SampleTests) BeforeEach(t *testing.T) {}
func (s *SampleTests) AfterEach(t *testing.T)  {}

func (s *SampleTests) SubTestCompare(t *testing.T) {
  if 1 != 1 {
    t.FailNow()
  }
}

func (s *SampleTests) SubTestCheckPrefix(t *testing.T) {
  if !strings.HasPrefix("abc", "ab") {
    t.FailNow()
  }
}

func TestSampleTests(t *testing.T) {
  gtest.RunSubTests(t, &SampleTests{})
}

Her biri farklı bir kurulum / sökme rutini seti kullanarak bir paket içinde istediğiniz herhangi bir test grubu olarak oluşturabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.