Menj be a legjobb gyakorlatokba - tesztelés

A programozási karrierem korai napjaiban nem láttam igazán az értéket, és elsősorban azt gondoltam, hogy ez megismétli a munkát. Most azonban általában 90-100% -os teszt lefedettségre törekszem mindennel, amit írok. És általában azt hiszem, hogy minden réteg tesztelése jó gyakorlat (erre visszatérünk).

Valójában, amikor megnézem a napi előttem lévő kódbázisokat, azok közül a legkevésbé attól tartok, amelyek megváltoztatása a legkevesebb. És ez végső soron csökkenti termelékenységemet és teljesítményeinket. Tehát számomra elég egyértelmű, hogy a magas teszt lefedettség magasabb minőséget és magasabb termelékenységet jelent.

Tesztelés minden rétegen

Azonnal belemerülünk egy példába. Tegyük fel, hogy van egy alkalmazás a következő szerkezettel.

Alkalmazási modell

Van néhány megosztott elem, például a modellek és a kezelők. Akkor néhányféle módon léphet kapcsolatba ezzel az alkalmazással, pl. CLI, HTTP API vagy Thrift RPC. Jó gyakorlatnak találtam, hogy ellenőrizze, hogy nem csak a modelleket vagy csak a kezelőket, hanem mindegyiket is teszteli. Még ugyanahhoz a szolgáltatáshoz. Mivel igaz, de feltétlenül igaz, hogy ha a kezelőben megvalósította a X Feature támogatását, akkor ez valójában például a HTTP és a Thrift interfészen keresztül érhető el.

Ekkor magabiztosabban fog változtatni logikájában, akár mélyen az alkalmazás magjában is.

Táblázat alapú tesztek

Szinte minden esetben, amikor egy módszert tesztel, szeretne kipróbálni egy-egy forgatókönyvet a funkcióval kapcsolatban. Általában eltérő bemeneti paraméterekkel vagy eltérő válaszjelekkel. Szeretem ezeket a teszteket egy Teszt * függvénybe csoportosítani, majd egy hurok fut az összes teszt esetén. Íme egy alapvető példa:

func TestDivision (t * tesztelés.T) {
    tesztek: = [] struct {
        x float64
        y float64
        eredmény float64
        hibás hiba
    } {
        {x: 1,0, y: 2,0, eredmény: 0,5, hiba: nulla},
        {x: -1,0, y: 2,0, eredmény: -0,5, hiba: nulla},
        {x: 1,0, y: 0,0, eredmény: 0,0, hiba: ErrZeroDivision},
    }
    _ esetén, teszt: = tartománytesztek {
        eredmény, hib: = oszt (teszt.x, teszt.y)
        assert.IsType (t, test.err, err)
        assert.Equal (t, teszt.redmény, eredmény)
    }
}

A fenti tesztek nem fednek le mindent, hanem példaként szolgálnak a várt eredmények és hibák tesztelésére. A fenti kód az állításokhoz a nagyszerű bizonyítócsomagot is használja.

Bővítés, tábla alapú tesztek megnevezett teszt esetekkel

Ha sok tesztje van, vagy gyakran új fejlesztők vannak, amelyek nem ismerik a kódot, akkor hasznos lehet a teszt elnevezése. Íme egy rövid példa arra, hogyan nézne ki ez

tesztek: = térkép [string] struct {
    szám int
    smsErr hiba
    hibás hiba
} {
    "sikeres": {0132423444, nulla, nulla},
    "szétosztja a hibát": {0132423444, sampleErr, sampleErr},
}

Vegye figyelembe, hogy itt különbség van a térkép és a szelet között. A térkép nem garantálja a rendelést, míg a szelet nem.

Gúnyolódás gúnyolódással

Az interfészek természetesen szuper jó integrációs pontok a tesztekhez, mivel egy interfész megvalósítása könnyen helyettesíthető egy ál-megvalósítással. A csúnya írás azonban nagyon unalmas és unalmas lehet. Az élet megkönnyítése érdekében gúnyolódást használok, hogy egy adott felület alapján generáljam gúnyolásaimat.

Vessen egy pillantást arra, hogyan lehet ezzel dolgozni. Tegyük fel, hogy a következő felülettel rendelkezik.

típusú SMS interfész {
    Küldés (szám int, szöveges karakter) hiba
}

Itt van egy próbabábu megvalósítás, amelyet az interfész használ:

// Az Messager különféle típusú üzenetküldést kezel.
írja be az Messager struct {
    sms SMS
}
// A SendHelloWorld Hello World SMS-t küld.
func (m * Messager) SendHelloWorld (szám int) hiba {
    err: = m.sms.Send (szám: "Helló, világ!")
    ha hibás! = nulla {
        visszatérési hiba
    }
    visszatérés nulla
}

Most a Mockery használatával álcázhatunk az SMS interfész számára. Így néz ki ez (ez a példa a -inpkg zászló használatával hozza létre a mintát ugyanabban a csomagban, mint az interfész).

// A MockSMS egy automatikusan generált modell az SMS típushoz
írja be a MockSMS struct {
    mock.Mock
}
// A Küldés egy ál-függvényt biztosít az adott mezőkkel: szám, szöveg
func (_m * MockSMS) Küldés (szám int, szöveges karakterlánc) hiba {
    ret: = _m.Callled (szám, szöveg)
    var r0 hiba
    ha rf, rendben: = ret.Get (0). (func (int, string) hiba); rendben {
        r0 = rf (szám, szöveg)
    } más {
        r0 = ret.hiba (0)
    }
    visszatérési r0
}
var _ SMS = (* MockSMS) (nulla)

Az SMS felépítés a Mock.Mock tanúsításából származik, ami érdekes lehetőségeket kínál nekünk a teszt esetek írásakor. Tehát itt az ideje, hogy megírjuk a SendHelloWorld módszer tesztjét a Mockery modellje felhasználásával.

func TestSendHelloWorld (t * tesztelés.T) {
    mintaErr: = hibák.Új ("némi hiba")
    tesztek: = térkép [string] struct {
        szám int
        smsErr hiba
        hibás hiba
    } {
        "sikeres": {0132423444, nulla, nulla},
        "szétosztja a hibát": {0132423444, sampleErr, sampleErr},
    }
    _ esetén, teszt: = tartománytesztek {
        sms: = & MockSMS {}
        sms.On ("Küldés", teszt.szám, "Hello, világ!"). Vissza (test.smsErr) .Once ()
        m: = & Messager {
            sms: sms,
        }
   
        err: = m.SendHelloWorld (tesztszám)
        assert.Equal (t, test.err, err)
        sms.AssertExpectations (t)
    }
}

Van néhány pont, amelyet érdemes megemlíteni a fenti példakódban. A teszt során észreveszi, hogy a MockSMS-t pillanatok el, majd a .On () használatával diktálhatom, hogy mi történjen (.Return ()) -nel, amikor bizonyos paramétereket elküldnek a modellnek.

Végül az sms.AssertExpectations segítségével ellenőrzem, hogy az SMS interfészt a várt számú alkalommal meghívták-e. Ebben az esetben egyszer ().

Az összes fenti fájl megtalálható ebben a lényegben.

Arany fájl tesztek

Bizonyos esetekben hasznosnak találtam, hogy azt állíthatom, hogy egy nagy válaszbuborék ugyanaz marad. Visszaadhatók adatok például egy API-tól származó JSON-adatokból. Erre az esetre megtanultam Michell Hashimoto-tól, hogy az arany fájlok és az intelligens eszközök együttesen használják-e a parancssori zászlók tesztelésének felfedését.

Az alapötlet az, hogy a helyes válaszrészt írná egy fájlba (az arany fájl). Ezután a tesztek futtatásakor bájtnyi összehasonlítást végez az arany fájl és a teszt válasz között.

A könnyebb megkönnyítés érdekében létrehoztam az aranycsomagot, amely átlátható módon kezeli a parancssori jelzőbeállítást, valamint az aranyfájlok írását és összehasonlítását.

Íme egy példa arra, hogyan lehet az goldie-t használni az ilyen típusú teszteléshez:

func TestExample (t * tesztelés.T) {
    felvevő: = httptest.NewRecorder ()

    req, err: = http.NewRequest ("GET", "/ példa", nulla)
    assert.Nil (t, hiba)

    kezelő: = http.HandlerFunc (PéldaHandler)
    handler.ServeHTTP ()

    goldie.Assert (t, "példa", recorder.Body.Bytes ())
}

Amikor frissítenie kell az arany fájlt, futtassa a következőt:

menjen teszt-frissítés. / ...

És amikor csak el akarja végezni a teszteket, akkor a szokásos módon csinálta:

menj próbára. / ...

Viszlát!

Köszönöm, hogy ragaszkodtál a végéhez! Remélem, találtál valami hasznosat a cikkben.