Viet
Viet

Reputation: 6953

How to test for error condition from invalid input

I'm trying to do a test:

The goal is to expect an Error if the input will cause an invalid result. I've learnt that it's not recommended to return an error string in Go. How can I expect an error whilst doing unit testing in Go?

Here's the code:

func arrIdx(arr []int, idx int) (int, error) {
    if idx >= 0 && idx < len(arr) {
        return arr[idx], nil
    }
    return 0, fmt.Errorf("This is an error")
}
func TestArrIdx(t *testing.T) {
    arr := []int{1, 2, 3, 4, 5}
    tests := []struct {
        arr []int
        idx  int
        expect int // or an error
    }{
        {arr, len(arr) - 1, 5, nil},
        {arr, 1, 2, nil},
        // {arr, -1, 0, t.Error},
    }
    for _, test := range tests {
        testName := fmt.Sprintf("%d", test.input)
        t.Run(testName, func(t *testing.T) {
            got := arr[test.input]
            if got != test.expect {
                t.Errorf("got %v, want %v", got, test.expect)
            } else {
                fmt.Printf("got %v expect %v\n", got, test.expect)
            }
        })
    }
}

Upvotes: 0

Views: 1531

Answers (1)

sorens
sorens

Reputation: 5060

I am not entirely sure what you are trying to do. Usually, you write methods that you want to test. Those methods have expected and unexpected results. You write unit tests to inject expected and unexpected input to generate those expected and unexpected results so your unit tests can make sure your methods are doing what they are built to do.

That being said, I could modify your block of code to handle errors, but I am not sure that is what you want:

package so_test

import (
    "fmt"
    "testing"
)

func TestArrIdx(t *testing.T) {
    arr := [5]int{1, 2, 3, 4, 5}
    tests := []struct {
        input  int
        expect int
        err error
    }{
        {len(arr) - 1, 5, nil},
        {1, 2, nil},
        {0, 0, fmt.Errorf("this is an error")},
    }
    for _, test := range tests {
        testName := fmt.Sprintf("%d", test.input)
        t.Run(testName, func(t *testing.T) {
            if test.err != nil {
                t.Errorf("received error '%v'", test.err)
            } else {
                got := arr[test.input]
                if got != test.expect {
                    t.Errorf("got %v, want %v", got, test.expect)
                } else {
                    fmt.Printf("got %v expect %v\n", got, test.expect)
                }
            }
        })
    }
}

Or, maybe this is closer to what you were hoping to do?

package so_test

import (
    "fmt"
    "testing"
)

// our unit test of the DoSomething() function
func TestDoSomething(t *testing.T) {
    tests := []struct {
        input  int
        expect int
        err error
    }{
        {1, 2, nil},
        {2, 2, fmt.Errorf("this is an error")}, 
        {0, 0, fmt.Errorf("this is a DIFFERENT error")}, // will always fail
    }
    for _, test := range tests {
        result, err := DoSomething(test.input)
        if test.err != nil {
            if err == nil {
                t.Errorf("expected error '%v', received none\n", test.err)
            } else if err.Error() != test.err.Error() {
                t.Errorf("expected error: '%v', received '%v'\n", test.err, err)
            } else {
                fmt.Printf("expected error '%v', received error: '%v'\n", test.err, err)
            }
        } else {
            if result != test.expect {
                t.Errorf("got %v, want %v", result, test.expect)
            } else {
                fmt.Printf("got %v expect %v\n", result, test.expect)
            }
        }
    }
}

// our function to test
func DoSomething(something int) (int, error) {
    if something == 1 {
        return 2, nil;
    }

    return 0, fmt.Errorf("this is an error")
}

Upvotes: 1

Related Questions