1
\$\begingroup\$

I have never done testing, and I have 5 end points which have methods such as GET, POST, and DELETE to test. I wrote test cases using the go's in built testing package. I'm worried that I'm missing some cases which are not striking to my mind. For example: I have an end point which goes like this:

a.Router.HandleFunc("/1/sig/id/{id:[0-9]+}", a.sigHandler).Methods("GET", "POST", "DELETE")

And the Test case goes like this:

func TestSigHandler(t *testing.T){

    test_cases := []string{"2021205"}  
    // GET Testing
    for _, method := range test_cases{
        usersUrl = fmt.Sprintf("%s/1/sig/id/%s", server.URL, method) //Grab the address for the API endpoint
        request, err := http.NewRequest("GET", usersUrl, nil)
        res, err := http.DefaultClient.Do(request)

        if err != nil {
            t.Error(err) //Something is wrong while sending request
        }

        if res.StatusCode != 200  {
            t.Errorf("Page not found : ", res.StatusCode) //Uh-oh this means our test failed
        }
    }

    // POST Testing
        sig := []byte( `{
        "raw": "a new sig"
    }`)
        usersUrl = fmt.Sprintf("%s/1/sig/id/2021205", server.URL) //Grab the address for the API endpoint
        request, err := http.NewRequest("POST", usersUrl, bytes.NewBuffer(sig))
        if err != nil{
            t.Error(err)
        }
        request.Header.Set("Content-Type", "application/json")
        res, err := http.DefaultClient.Do(request)

        if err != nil {
            t.Error(err) //Something is wrong while sending request
        }

        if res.StatusCode != 200  {
            t.Errorf(" Internal Server Error: ", res.StatusCode) //Uh-oh this means our test failed
        }

        // DELETE Testing

        sigs_delete_cases := []string{ "1000345"}
        for _, sig_to_be_deleted := range sigs_delete_cases{
            usersUrl = fmt.Sprintf("%s/1/sig/id/%s", server.URL, sig_to_be_deleted) //Grab the address for the API endpoint
            request, err := http.NewRequest("DELETE", usersUrl, nil)
            res, err := http.DefaultClient.Do(request)

            if err != nil {
                t.Error(err) //Something is wrong while sending request
            }

            if res.StatusCode != 200  {
                t.Errorf("Tried to delete a reserved Id : ", res.StatusCode) //Uh-oh this means our test failed
            }
        }


}

The problem is that, most of my test cases follow similar pattern where I post data in different formats and checking the response codes. I strongly feel like I'm missing something that will break my API when I push it to prod. I need some insight on testing these routes so that I can be confident to push the api to prod.

And my main test function goes like this:

func TestMain(m *testing.M) {
    server = httptest.NewServer(a.InitializeRouter())
    a.InitializeDB(fmt.Sprintf("postgres://****:****@localhost/db?sslmode=disable", os.Getenv("POSTGRES_URL")))
    code := m.Run()
    if code == 0 {
        fmt.Println("Success!")
    } else {
        fmt.Println("One or more Test Cases have failed! Please use  go test -v  for more details")
    }

    os.Exit(code)
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Could be interesting for you: youtu.be/hVFEV-ieeew (was published yesterday) \$\endgroup\$ – oliverpool Jul 25 '17 at 7:58
  • \$\begingroup\$ What is a ? (Is suppose that you use a library ?) \$\endgroup\$ – oliverpool Jul 25 '17 at 8:02
  • \$\begingroup\$ @oliverpool a is the instance for the struct 'App' in the main.go where I'm initializing the router and db instances. \$\endgroup\$ – SKSV Jul 25 '17 at 16:38
  • \$\begingroup\$ @oliverpool Great! \$\endgroup\$ – SKSV Jul 25 '17 at 16:40
2
\$\begingroup\$

Using the tips of the justforfunc #16 video, you could make your tests using a table (tt = testing table) and then iterate on it (tc = test case).

Your test could then look like this:

func TestSigHandler(t *testing.T) {
    jsonHeaders := map[string]string{"Content-Type": "application/json"}

    tt := []struct {
        name    string
        method  string
        uri     string
        payload []byte
        headers map[string]string
    }{
        {"get item", "GET", "/1/sig/id/2021205", nil, nil},
        {"post new item", "POST", "/1/sig/id/2021205", []byte(`{"raw": "a new sig"}`), jsonHeaders},
        {"delete item", "DELETE", "/1/sig/id/1000345", nil, nil},
    }

    for _, tc := range tt {
        t.Run(tc.name, func(t *testing.T) {
            var payload io.Reader
            if tc.payload != nil {
                payload = bytes.NewBuffer(tc.payload)
            }
            req, err := http.NewRequest(tc.method, server.URL+tc.uri, payload)
            if err != nil {
                t.Fatalf("could not create request: %v", err)
            }

            for key, value := range tc.headers {
                req.Header.Set(key, value)
            }

            res, err := http.DefaultClient.Do(req)
            if err != nil {
                t.Fatalf("could not send request: %v", err)
            }

            if res.StatusCode != http.StatusOK {
                t.Fatalf("expected status OK; got %v", res.Status)
            }
        })
    }
}

Looking at your TestMain, I have the feeling that you are doing only integration tests (from the request to the database).

You should split you tests to the individual layers (the current one if for routing: it could use a mocked DB).

With this approach, the TestMain could be deleted (do only the setup that is necessary for each test).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.