A Go micro-framework to help writing RESTful API integration test
Package RESTit provides helps to those who want to write an integration test program for their JSON-based RESTful APIs.
The aim is to make these integration readable highly re-usable, and yet easy to modify.
Less is more. The main theme of RESTit v2 is:
- To reduce code for testing.
- Make tests reusable.
This version has several improvements over v1:
- No longer need to define protocol before testing.
- Generates *http.Request directly. No longer depend on the napping library. One less thing to learn.
- Support httptest.ResponseRecorder test and ordinary TCP tests. Tests are nearly identical for both. More flexibility.
- New lzjson JSON decoding library allow you to examine a JSON value without predefined static structure. It also allow you to partially decode a JSON value (e.g. "posts" field in it).
RESTit (v2) supports httptest.ResponseRecorder based testing. In which approach, you don't need to run a server that listens to TCP port. You only need an http.Handler to start testing.
package post_test
import (
"post"
"testing"
"net/http/httptest"
restit "github.com/go-restit/restit/v2"
)
func TestPostAPI(t *testing.T)
var err error
var resp restit.Response
// define the path for your handler
service := restit.NewHTTPTestService("/dummy/api", post.Handler)
token := "some_access_token"
post1 := Post{ID: "1234", Name: "hello world", Body: "some hello world message"}
resp, err = service.Create(post1, "/posts").
AddQuery("access_token", token).
AddHeader("User-Agent", "RESTit tester").
Expect(expectation1).
Expect(expectation2).
Expect(expectation3).
Do()
if err != nil {
t.Error(err.Error())
}
resp, err = service.Retrieve("/post/1234").
AddHeader("User-Agent", "RESTit tester").
Expect(expectation1).
Expect(expectation2).
Expect(expectation3).
Do()
if err != nil {
t.Error(err.Error())
}
resp, err = service.List("/posts").
AddHeader("User-Agent", "RESTit tester").
Expect(expectation1).
Expect(expectation2).
Expect(expectation3).
Do()
if err != nil {
t.Error(err.Error())
}
post2 := Post{ID: "1234", Name: "updated", Body: "some updated message"}
resp, err = service.Update(post2, "/post/1234").
AddHeader("User-Agent", "RESTit tester").
AddQuery("access_token", token).
Expect(expectation1).
Expect(expectation2).
Expect(expectation3).
Do()
if err != nil {
t.Error(err.Error())
}
resp, err = service.Delete("/post/1234").
AddHeader("User-Agent", "RESTit tester").
AddQuery("access_token", token).
Expect(expectation1).
Expect(expectation2).
Expect(expectation3).
Do()
if err != nil {
t.Error(err.Error())
}
}
We use raw *http.Request in our test case. Feel free to manipulate it before doing the test:
caseCreate := service.Create(Post{Name: "hello world", Body: "some hello world message"}).
Expect(expectation1).
Expect(expectation2).
Expect(expectation3)
caseCreate.Request.Header.Add("X-Custom-Header", "Hello World")
caseCreate.Do()
The request and response are fully examinable:
caseCreate := service.Create(Post{Name: "hello world", Body: "some hello world message"}).
Expect(expectation1).
Expect(expectation2).
Expect(expectation3)
resp, err := caseCreate.Do()
if err != nil {
// raw request
t.Logf("request: %#v", caseCreate.Request) // raw *htt.Request used
// read the raw response body
bodyBytes, _ := ioutil.ReadAll(resp.Body())
t.Logf("body: %s", bodyBytes)
t.Errorf("error running create %s", err)
return
}
Expectation is an interface for you to implement.
You can write your own expectation easily with Describe.
import (
restit "github.com/go-restit/restit/v2"
"golang.org/x/net/context"
)
...
exp1 := restit.Describe("test something", func(ctx context.Context, resp restit.Response) error {
// examine body
body := resp.Body()
// parse the body as lzjson.Node
json := resp.JSON()
return nil
})
caseCreate := service.Create(Post{Name: "hello world", Body: "some hello world message"}).
Expect(exp1)
Or you can use the test helpers (like StatusCodeIs, LengthIs and DescribeJSON):
caseCreate := service.Create(Post{Name: "hello world", Body: "some hello world message"}).
Expect(restit.StatusCodeIs(http.StatusOK)).
Expect(restit.LengthIs("posts", 1)).
Expect(restit.Nth(0).Of("posts").Is(restit.DescribeJSON(
"item #0 retrieved has the values", func (json lzjson.Node) (err error) {
post := &Post{}
if err = json.Unmarshal(post); err != nil {
return
}
if want, have := "myid", post.ID; want != have {
err = fmt.Errorf("ID expected %#v, got %#v", want, have)
}
return
})))
RESTit uses the helper libaray lzjson to help parse JSON response.
Features:
- You don't need to define a protocol struct before decoding JSON.
- You can examine values existance, types before decoding.
- You can also decode only part of JSON.
More information, please see lzjson respository
To report issue, please visit the issue tracker.
And of course, patches and pull requests are most welcome.