पैनिक का परीक्षण कैसे करें?


90

मैं वर्तमान में विचार कर रहा हूं कि परीक्षणों को कैसे लिखा जाए जो जांचता है कि क्या कोड का एक टुकड़ा घबराया हुआ है? मुझे पता है कि गो recoverपैनिक को पकड़ने के लिए उपयोग करता है, लेकिन जावा कोड के विपरीत, आप वास्तव में यह निर्दिष्ट नहीं कर सकते हैं कि घबराहट की स्थिति में आपको क्या कोड छोड़ देना चाहिए या आपके पास क्या है। तो अगर मेरे पास कोई फ़ंक्शन है:

func f(t *testing.T) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    OtherFunctionThatPanics()
    t.Errorf("The code did not panic")
}

मैं वास्तव में नहीं बता सकता कि क्या OtherFunctionThatPanicsहम घबरा गए और हम ठीक हो गए, या यदि फ़ंक्शन बिल्कुल भी घबराया नहीं था। यदि कोई घबराहट नहीं है और घबराहट होने पर किस कोड को निष्पादित करने के लिए मैं किस कोड को छोड़ दूं? मैं कैसे जाँच सकता हूँ कि वहाँ से कुछ घबराहट हुई थी?

जवाबों:


106

testingवास्तव में "सफलता" की अवधारणा नहीं है, केवल असफलता। तो ऊपर आपका कोड सही है। आपको यह शैली थोड़ी अधिक स्पष्ट लग सकती है, लेकिन यह मूल रूप से एक ही बात है।

func TestPanic(t *testing.T) {
    defer func() {
        if r := recover(); r == nil {
            t.Errorf("The code did not panic")
        }
    }()

    // The following is the code under test
    OtherFunctionThatPanics()
}

मैं आमतौर पर testingकाफी कमजोर होता हूं । आपको जिन्कगो जैसे अधिक शक्तिशाली परीक्षण इंजनों में रुचि हो सकती है । यहां तक ​​कि अगर आप पूर्ण जिन्कगो प्रणाली नहीं चाहते हैं, तो आप बस इसके मिलानकर्ता पुस्तकालय, गोमेगा का उपयोग कर सकते हैं , जिसका उपयोग किया जा सकता है testing। गोमेगा में मैचर्स शामिल हैं:

Expect(OtherFunctionThatPanics).To(Panic())

आप एक साधारण फ़ंक्शन में पैनिक-चेकिंग भी लपेट सकते हैं:

func TestPanic(t *testing.T) {
    assertPanic(t, OtherFunctionThatPanics)
}

func assertPanic(t *testing.T, f func()) {
    defer func() {
        if r := recover(); r == nil {
            t.Errorf("The code did not panic")
        }
    }()
    f()
}

@IgorMikushkin गो 1.11 में, रोब नेपियर द्वारा वर्णित पहले रूप का उपयोग करके वास्तव में कवरेज के लिए काम करता है।
एफजीएम

क्या कोई कारण है जो आप उपयोग करते हैं r := recover(); r == nilऔर न कि बस recover() == nil?
डंकन जोन्स

@ डंकनजोन वास्तव में इस मामले में नहीं हैं। यह ब्लॉक में उपलब्ध त्रुटि बनाने के लिए एक बहुत ही विशिष्ट गो पैटर्न है, इसलिए ओपी के लिए शायद यह उस तरह से लिखने की आदत थी (और मैं उनके कोड को आगे लाया), लेकिन यह वास्तव में इस मामले में उपयोग नहीं किया गया है।
रोब नेपियर

43

यदि आप गवाही / जोर देते हैं , तो यह एक-लाइनर है:

func TestOtherFunctionThatPanics(t *testing.T) {
  assert.Panics(t, OtherFunctionThatPanics, "The code did not panic")
}

या, यदि आपके OtherFunctionThatPanicsपास इसके अलावा कोई हस्ताक्षर है func():

func TestOtherFunctionThatPanics(t *testing.T) {
  assert.Panics(t, func() { OtherFunctionThatPanics(arg) }, "The code did not panic")
}

यदि आपने अभी तक गवाही देने की कोशिश नहीं की है, तो गवाही / मॉक भी देखें । सुपर सिंपल एसेसरीज और मॉक।


7

जब मैं कई परीक्षण मामलों की जांच कर रहा हूं, तो मैं इस तरह से कुछ करूंगा:

package main

import (
    "reflect"
    "testing"
)


func TestYourFunc(t *testing.T) {
    type args struct {
        arg1 int
        arg2 int
        arg3 int
    }
    tests := []struct {
        name      string
        args      args
        want      []int
        wantErr   bool
        wantPanic bool
    }{
        //TODO: write test cases
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            defer func() {
                r := recover()
                if (r != nil) != tt.wantPanic {
                    t.Errorf("SequenceInt() recover = %v, wantPanic = %v", r, tt.wantPanic)
                }
            }()
            got, err := YourFunc(tt.args.arg1, tt.args.arg2, tt.args.arg3)
            if (err != nil) != tt.wantErr {
                t.Errorf("YourFunc() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("YourFunc() = %v, want %v", got, tt.want)
            }
        })
    }
}

खेल का मैदान जाओ


4

जब आपको आतंक की सामग्री की जांच करने की आवश्यकता होती है, तो आप पुनर्प्राप्त मूल्य को टाइप कर सकते हैं:

func TestIsAheadComparedToPanicsWithDifferingStreams(t *testing.T) {
    defer func() {
        err := recover().(error)

        if err.Error() != "Cursor: cannot compare cursors from different streams" {
            t.Fatalf("Wrong panic message: %s", err.Error())
        }
    }()

    c1 := CursorFromserializedMust("/foo:0:0")
    c2 := CursorFromserializedMust("/bar:0:0")

    // must panic
    c1.IsAheadComparedTo(c2)
}

यदि आप जिस कोड का परीक्षण कर रहे हैं वह घबराता नहीं है या त्रुटि से घबराता है या आपके द्वारा अपेक्षित त्रुटि संदेश से घबराता है, तो परीक्षण विफल हो जाएगा (जो आप चाहते हैं)।


1
यह त्रुटि संदेशों की तुलना करने के लिए एक विशिष्ट त्रुटि प्रकार (जैसे os.SyscallError) पर टाइप-एस्टर के लिए अधिक मजबूत है, जो एक गो रिलीज से अगले तक बदल सकता है (जैसे)।
माइकल

जब कोई विशिष्ट प्रकार उपलब्ध हो, तो माइकल अगस्त, शायद यही बेहतर तरीका है।
joonas.fi

3

आपके मामले में आप यह कर सकते हैं:

func f(t *testing.T) {
    recovered := func() (r bool) {
        defer func() {
            if r := recover(); r != nil {
                r = true
            }
        }()
        OtherFunctionThatPanics()
        // NOT BE EXECUTED IF PANICS
        // ....
    }
    if ! recovered() {
        t.Errorf("The code did not panic")

        // EXECUTED IF PANICS
        // ....
    }
}

एक सामान्य आतंक राउटर फ़ंक्शन के रूप में यह भी काम करेगा:

https://github.com/7d4b9/recover

package recover

func Recovered(IfPanic, Else func(), Then func(recover interface{})) (recoverElse interface{}) {
    defer func() {
        if r := recover(); r != nil {
            {
                // EXECUTED IF PANICS
                if Then != nil {
                    Then(r)
                }
            }
        }
    }()

    IfPanic()

    {
        // NOT BE EXECUTED IF PANICS
        if Else != nil {
            defer func() {
                recoverElse = recover()
            }()
            Else()
        }
    }
    return
}

var testError = errors.New("expected error")

func TestRecover(t *testing.T) {
    Recovered(
        func() {
            panic(testError)
        },
        func() {
            t.Errorf("The code did not panic")
        },
        func(r interface{}) {
            if err := r.(error); err != nil {
                assert.Error(t, testError, err)
                return
            }
            t.Errorf("The code did an unexpected panic")
        },
    )
}

3

सक्सेस वे

मेरे लिए, नीचे दिए गए समाधान को पढ़ना आसान है और आपको परीक्षण के तहत कोड का प्राकृतिक कोड प्रवाह दिखाता है।

func TestPanic(t *testing.T) {
    // No need to check whether `recover()` is nil. Just turn off the panic.
    defer func() { recover() }()

    OtherFunctionThatPanics()

    // Never reaches here if `OtherFunctionThatPanics` panics.
    t.Errorf("did not panic")
}

अधिक सामान्य समाधान के लिए, आप इसे इस तरह भी कर सकते हैं:

func TestPanic(t *testing.T) {
    shouldPanic(t, OtherFunctionThatPanics)
}

func shouldPanic(t *testing.T, f func()) {
    defer func() { recover() }()
    f()
    t.Errorf("should have panicked")
}

0

आप पैनिक इनपुट देकर घबरा सकते हैं कि कौन सा फ़ंक्शन टेस्ट कर सकता है

package main

import "fmt"

func explode() {
    // Cause a panic.
    panic("WRONG")
}

func explode1() {
    // Cause a panic.
    panic("WRONG1")
}

func main() {
    // Handle errors in defer func with recover.
    defer func() {
        if r := recover(); r != nil {
            var ok bool
            err, ok := r.(error)
            if !ok {
                err = fmt.Errorf("pkg: %v", r)
                fmt.Println(err)
            }
        }

    }()
    // These causes an error. change between these
    explode()
    //explode1()

    fmt.Println("Everything fine")

}

http://play.golang.org/p/ORWBqmPSVA

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.