Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fixtures in Golang testing

Coming from the python world, fixtures are very useful (Fixtures defines a Python contract for reusable state / support logic, primarily for unit testing). I was wondering if there's similar support in Golang which can allow me to run my tests with some predefined fixtures like setting up server, tearing it down, doing some repeated tasks each time a test is run ? Can someone point me to some examples of doing the same in Golang ?

like image 358
psbits Avatar asked Jan 20 '16 00:01

psbits


2 Answers

If you want to use the standard Go testing tools, you can define a function with the signature TestMain(m *testing.M) and put your fixture code in there.

From the testing package wiki:

It is sometimes necessary for a test program to do extra setup or teardown before or after testing. It is also sometimes necessary for a test to control which code runs on the main thread. To support these and other cases, if a test file contains a function:

func TestMain(m *testing.M)

then the generated test will call TestMain(m) instead of running the tests directly. TestMain runs in the main goroutine and can do whatever setup and teardown is necessary around a call to m.Run. It should then call os.Exit with the result of m.Run. When TestMain is called, flag.Parse has not been run. If TestMain depends on command-line flags, including those of the testing package, it should call flag.Parse explicitly.

A simple implementation of TestMain is:

func TestMain(m *testing.M) {
    flag.Parse()
    os.Exit(m.Run())
}
like image 173
alecdwm Avatar answered Nov 07 '22 01:11

alecdwm


I know this is an old question, but this still came up in a search result so I thought I'd give a possible answer.

You can isolate code out to helper functions that return a "teardown" function to clean up after itself. Here's one possible way to go about starting a server and have it close at the end of the test case.

func setUpServer() (string, func()) {
    h := func(w http.ResponseWriter, r *http.Request) {
        code := http.StatusTeapot
        http.Error(w, http.StatusText(code), code)
    }

    ts := httptest.NewServer(http.HandlerFunc(h))
    return ts.URL, ts.Close
}

func TestWithServer(t *testing.T) {
    u, close := setUpServer()
    defer close()

    rsp, err := http.Get(u)
    assert.Nil(t, err)
    assert.Equal(t, http.StatusTeapot, rsp.StatusCode)
}

This starts up a server with net/http/httptest and returns its URL along with a function that acts as the "teardown." This function is added to the defer stack so it's always called regardless of how the test case exits.

Optionally, you can pass in the *testing.T if you have a more complicated set up going on in there and you need to handle errors. This example shows the set up function returning a *url.URL instead of a URL formatted string, and the parse can possibly return an error.

func setUpServer(t *testing.T) (*url.URL, func()) {
    h := func(w http.ResponseWriter, r *http.Request) {
        code := http.StatusTeapot
        http.Error(w, http.StatusText(code), code)
    }

    ts := httptest.NewServer(http.HandlerFunc(h))
    u, err := url.Parse(ts.URL)
    assert.Nil(t, err)
    return u, ts.Close
}

func TestWithServer(t *testing.T) {
    u, close := setUpServer(t)
    defer close()

    u.Path = "/a/b/c/d"
    rsp, err := http.Get(u.String())
    assert.Nil(t, err)
    assert.Equal(t, http.StatusTeapot, rsp.StatusCode)
}
like image 29
James Avatar answered Nov 06 '22 23:11

James