restit

package
v0.0.0-...-63488a3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2023 License: GPL-3.0 Imports: 11 Imported by: 0

README

RESTit (v1) Godoc Travis test Appveyor test

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.

Migration Notice

This is an old version of RESTit library. This is to keep older projects that depending RESTit from breaking. For new projects, please use the latest version.

If you were using v1 and want to keep using it, please change your import statement from:

import (
    "github.com/yookoala/restit"
)

to:

import (
    restit "github.com/go-restit/restit/v1"
)

Dependencies

RESTit is written in Go (a.k.a. Golang). You'll have to install go first.

You need to first install "github.com/jmcvetta/napping":

$ go get github.com/jmcvetta/napping

Install

Just like installing other golang packages:

$ go get github.com/go-restit/restit/v1

How to Use

To use, you first have to implement the TestRespond interface for the REST server response that you're expecting.

In Go, json contents are usually unmarshaled to structs. What you need to do is implement 4 methods to the struct type.

Example:

type ExmplResp struct {
	Status string  `json:"status"`
	Result []Stuff `json:"result"`
}

func (r *ExmplResp) Count() int {
	return len(r.Result)
}

func (r *ExmplResp) NthValid(n int) (err error) {
	// some test to see nth record is valid
	// such as:
	if r.Result[n].StuffId == 0 {
		return fmt.Errorf("The thing has a StuffId = 0")
	}
	return
}

func (r *ExmplResp) GetNth(n int) (item interface{}, err error) {
	// return the nth item
	return Result[n]
}

func (r *ExmplResp) Match(a interface{}, b interface{}) (match bool, err error) {

	// cast a and b back to Stuff
	real_a = a.(Stuff)
	real_b = b.(Stuff)

	// some test to check if real_a equals real_b
	// ...
}

func (r *ExmplResp) Reset() {
    r.Status = ""
    r.Result = make([]Stuff, 0)
}

Then you may write the tests.

Write Your Tests

You can then test your RESTful API like this:

import (
    restit "github.com/go-restit/restit/v1"
)

// create a tester for your stuffAPI
// first parameter is a human readable name that will
// appear on error second parameter is the base URL to
// the API entry point
stuffAPI := restit.Rest(
    "Stuff", "http://foobar:8080/api/stuffs")

// some parameters we'll use
var result restit.TestResult
var test restit.TestCase
var response ExmplResp

// some random stuff for test
// for example,
stuffToCreate = Stuff{
    Name: "apple",
    Color: "red",
}
stuffToUpdate = Stuff{
    Name: "orange",
    Color: "orange",
}

// here we add some dummy security measures
// or you may add any parameters you like
securityInfo := napping.Params{
    "username": "valid_user",
    "token": "some_security_token",
}


// -------- Test Create --------
// 1. create the stuff
test = stuffAPI.
    Create(&stuffToCreate).
    AddHeader("Authorization", "Some token").
    WithParams(&securityInfo).
    WithResponseAs(&response).
    ExpectResultCount(1).
    ExpectResultsValid().
    ExpectResultNth(0, &stuffToCreate).
    ExpectResultsToPass(
        "Custom Test",
        func (r Response) error {
        // some custom test you may want to run
        // ...
        })

result, err := test.Run()
if err != nil {
    // you may add more verbose output for
    // inspection
    fmt.Printf("Failed creating stuff!!!!\n")
    fmt.Printf("Please inspect the Raw Response: "
            + result.RawText())

    // or you can simply:
    panic(err)
}

// id of the just created stuff
// for reference of later tests
stuffId := response.Result[0].StuffId
stuffToCreate.StuffId = stuffId
stuffToUpdate.StuffId = stuffId

// 2. test the created stuff
test = stuffAPI.
    Retrieve(fmt.Sprintf("%d", stuffId)).
    WithResponseAs(&response).
    ExpectResultCount(1).
    ExpectResultsValid().
    ExpectResultNth(0, &stuffToCreate)
// A short hand to just panic on any error
result = test.RunOrPanic()


// -------- Test Update --------
// 1. update the stuff
result = stuffAPI.
    Update(&stuffToUpdate,
        fmt.Sprintf("%d", stuffId)).
    WithResponseAs(&response).
    WithParams(&securityInfo).
    ExpectResultCount(1).
    ExpectResultsValid().
    ExpectResultNth(0, &stuffToUpdate).
    RunOrPanic() // Yes, you can be this lazy

// 2. test the updated stuff
result = stuffAPI.
    Retrieve(fmt.Sprintf("%d", stuffId)).
    WithResponseAs(&response).
    ExpectResultCount(1).
    ExpectResultsValid().
    ExpectResultNth(0, &stuffToUpdate).
    RunOrPanic()


// -------- Test Delete --------
// delete the stuff
result = stuffAPI.
    Delete(fmt.Sprintf("%d", stuffId)).
    WithResponseAs(&response).
    WithParams(security).
    ExpectResultCount(1).
    ExpectResultsValid().
    ExpectResultNth(0, &stuffToUpdate).
    RunOrPanic()

// 2. test the deleted stuff
result = stuffAPI.
    Retrieve(fmt.Sprintf("%d", stuffId)).
    WithResponseAs(&response).
    ExpectStatus(404).
    RunOrPanic()

Bug Reports

To report issue, please visit the issue tracker.

And of course, patches and pull requests are most welcome.

Documentation

Overview

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.

To use, you first have to implement the `TestRespond` interface for the REST server response that you're expecting.

In Go, json contents are usually unmarshaled to structs. What you need to do is implement 4 methods to the struct type.

For example:

type ExmplResp struct {
	Status string  `json:"status"`
	Result []Stuff `json:"result"`
}

func (r *ExmplResp) Count() int {
	return len(r.Result)
}

func (r *ExmplResp) NthValid(n int) (err error) {
	// some test to see nth record is valid
	// such as:
	if r.Result[n].StuffId == 0 {
		return fmt.Errorf(
			"StuffId should not be 0")
	}
	return
}

func (r *ExmplResp) GetNth(n int) (item interface{},
	err error) {
	// return the nth item
	return Result[n]
}

func (r *ExmplResp) Match(
	a interface{}, b interface{}) (
	match bool, err error) {

	// cast a and b back to Stuff
	real_a = a.(Stuff)
	real_b = b.(Stuff)

	// some test to check if real_a equals real_b
	// ...
}

func (r *ExmplResp) Reset() {
	r.Status = ""
	r.Result = make([]Stuff, 0)
}

You can then test your RESTful API like this:

import restit "github.com/go-restit/restit/v1"

// create a tester for your stuff
// first parameter is a human readable name that will
// appear on error second parameter is the base URL to
// the API entry point
stuff := restit.Rest(
	"Stuff", "http://foobar:8080/api/stuffs")

// some parameters we'll use
var result restit.TestResult
var test restit.TestCase
var response ExmplResp

// some random stuff for test
// for example,
stuffToCreate = Stuff{
	Name: "apple",
	Color: "red",
}
stuffToUpdate = Stuff{
	Name: "orange",
	Color: "orange",
}

// here we add some dummy security measures
// or you may add any parameters you like
securityInfo := napping.Params{
	"username": "valid_user",
	"token": "some_security_token",
}

// -------- Test Create --------
// 1. create the stuff
test = stuffAPI.
	Create(&stuffToCreate).
	AddHeader("Authorization", "Some token").
	WithParams(&securityInfo).
	WithResponseAs(&response).
	ExpectResultCount(1).
	ExpectResultsValid().
	ExpectResultNth(0, &stuffToCreate).
	ExpectResultsToPass(
		"Custom Test",
		func (r Response) error {
		// some custom test you may want to run
		// ...
	})

result, err := test.Run()
if err != nil {
	// you may add more verbose output for
	// inspection
	fmt.Printf("Failed creating stuff!!!!\n")
	fmt.Printf("Please inspect the Raw Response: "
			+ result.RawText())

	// or you can simply:
	panic(err)
}

// id of the just created stuff
// for reference of later tests
stuffId := response.Result[0].StuffId
stuffToCreate.StuffId = stuffId
stuffToUpdate.StuffId = stuffId

// 2. test the created stuff
test = stuffAPI.
	TestRetrieve(fmt.Sprintf("%d", stuffId)).
	WithResponseAs(&response).
	ExpectResultCount(1).
	ExpectResultsValid().
	ExpectResultNth(0, &stuffToCreate)
// A short hand to just panic on any error
result = test.RunOrPanic()

// -------- Test Update --------
// 1. update the stuff
result = stuffAPI.
	Update(&stuffToUpdate,
		fmt.Sprintf("%d", stuffId)).
	WithResponseAs(&response).
	WithParams(&securityInfo).
	ExpectResultCount(1).
	ExpectResultsValid().
	ExpectResultNth(0, &stuffToUpdate).
	RunOrPanic() // Yes, you can be this lazy

// 2. test the updated stuff
result = stuffAPI.
	Retrieve(fmt.Sprintf("%d", stuffId)).
	WithResponseAs(&response).
	ExpectResultCount(1).
	ExpectResultsValid().
	ExpectResultNth(0, &stuffToUpdate).
	RunOrPanic()

// -------- Test Delete --------
// delete the stuff
result = stuffAPI.
	Delete(fmt.Sprintf("%d", stuffId)).
	WithResponseAs(&response).
	WithParams(security).
	ExpectResultCount(1).
	ExpectResultsValid().
	ExpectResultNth(0, &stuffToUpdate).
	RunOrPanic()

// 2. test the deleted stuff
result = stuffAPI.
	Retrieve(fmt.Sprintf("%d", stuffId)).
	WithResponseAs(&response).
	ExpectStatus(404).
	RunOrPanic()

Index

Constants

This section is empty.

Variables

View Source
var ErrorInvalidMatcher = errors.New("Matcher not valid")
View Source
var ErrorInvalidType = errors.New("Response item type is not struct")
View Source
var ErrorInvalidValidator = errors.New("Validator not valid")
View Source
var ErrorNoMatcher = errors.New("Matcher not found. Please set matcher using SetMatcher")
View Source
var ErrorNoValidator = errors.New("Validator not found. Please set validator using SetValidator")
View Source
var ErrorNthNotFound = errors.New("Nth item not found")
View Source
var ErrorUnexpectedItem = errors.New("At least one decoded json item is not map[string]interface{}")

Functions

This section is empty.

Types

type Case

type Case struct {
	Request      *napping.Request
	Name         string
	Session      Session
	Expectations []Expectation
	Tester       *Tester
	Result       *Result
}

func (*Case) AddHeader

func (c *Case) AddHeader(key, value string) *Case

Add a header parameter

func (*Case) ExpectResultCount

func (c *Case) ExpectResultCount(n int) *Case

Append Test to Expectations Tests if the result count equal to n

func (*Case) ExpectResultCountNot

func (c *Case) ExpectResultCountNot(n int) *Case

Append Test to Expectations Tests if the result count not equal to n

func (*Case) ExpectResultNth

func (c *Case) ExpectResultNth(n int, b interface{}) *Case

Append Test to Expectation Tests if the nth item matches the provided one

func (*Case) ExpectResultsToPass

func (c *Case) ExpectResultsToPass(
	desc string, test func(Response) error) *Case

Append Custom Test to Expectation Allow user to inject user defined tests

func (*Case) ExpectResultsValid

func (c *Case) ExpectResultsValid() *Case

Append Test to Expectations Tests if the item is valid

func (*Case) ExpectStatus

func (c *Case) ExpectStatus(ec int) *Case

Append Test to Expectations Tests if the response status is

func (*Case) InitForRun

func (c *Case) InitForRun() *Case

short hand to initialize and enable defaults even if tester is not present

func (*Case) Run

func (c *Case) Run() (r *Result, err error)

To actually run the test case

func (*Case) RunOrPanic

func (c *Case) RunOrPanic() (r *Result)

To run the test case and panic on error

func (*Case) WithErrorAs

func (c *Case) WithErrorAs(e Response) *Case

Set the error message to the given interface{}

func (*Case) WithParams

func (c *Case) WithParams(p *url.Values) *Case

Set the query parameter

func (*Case) WithResponseAs

func (c *Case) WithResponseAs(r Response) *Case

Set the result to the given interface{}

type DefaultResponse

type DefaultResponse map[string]interface{}

DefaultResponse is the default response type to use

func NewResponse

func NewResponse(n string, t interface{}) *DefaultResponse

NewResponse create a new DefaultResponse

func (*DefaultResponse) Count

func (p *DefaultResponse) Count() int

Count counts the number of items in the result

func (*DefaultResponse) GetNth

func (p *DefaultResponse) GetNth(n int) (ret interface{}, err error)

GetNth get the Nth item in the DefaultResponse

func (*DefaultResponse) Match

func (p *DefaultResponse) Match(a interface{}, b interface{}) (err error)

Match test if the nth item is valid

func (*DefaultResponse) NthValid

func (p *DefaultResponse) NthValid(n int) (err error)

NthValid test if the nth item is valid

func (*DefaultResponse) Reset

func (p *DefaultResponse) Reset()

Reset the value of this response

func (*DefaultResponse) SetMatcher

func (p *DefaultResponse) SetMatcher(in func(interface{}, interface{}) error)

MatchWith set the matcher function. If no matcher function is given, the test will by default fail

func (*DefaultResponse) SetValidator

func (p *DefaultResponse) SetValidator(in func(interface{}) error)

ValidateWith set the validator function. If no validator function is given, the test will by default fail

type Expectation

type Expectation struct {
	Desc string
	Test func(Response) error
}

Expection to the response in a Case

type Matcher

type Matcher func(interface{}, interface{}) error

type Response

type Response interface {

	// count the number of items in the result
	Count() int

	// test if the nth item is valid
	NthValid(int) error

	// test get the nth item
	GetNth(int) (interface{}, error)

	// test if the given 2 items matches
	Match(interface{}, interface{}) error

	// reset the result, prevent the problem of null result
	Reset()
}

Response needed to fulfill this interface in order to be tested by RESTit

type Result

type Result struct {
	Response *napping.Response
}

Test Result of a Case

type Session

type Session interface {
	Send(*napping.Request) (*napping.Response, error)
}

Wrap the napping.Session in Session to make unit testing easier

type Tester

type Tester struct {
	Name  string
	Url   string
	Trace *log.Logger
	Err   *log.Logger
}

Tester represents an ordinary RESTful entry point

func Rest

func Rest(name string, url string) *Tester

Create a tester for an API entry point name string human-readable name of the entry point baseUrl string RESTful API base url

func (*Tester) Create

func (t *Tester) Create(payload interface{}) *Case

Create Case to Create something with the payload

func (*Tester) Delete

func (t *Tester) Delete(id string) *Case

Create Case to Delete something of the id

func (*Tester) List

func (t *Tester) List(path ...string) *Case

Create Case to List something with the id string

func (*Tester) LogDefault

func (t *Tester) LogDefault() *Tester

setup default log environment

func (*Tester) LogErrTo

func (t *Tester) LogErrTo(l *log.Logger) *Tester

Add error logger

func (*Tester) LogTraceTo

func (t *Tester) LogTraceTo(l *log.Logger) *Tester

Add trace logger

func (*Tester) Retrieve

func (t *Tester) Retrieve(id string) *Case

Create Case to Retrieve something with the id string

func (*Tester) Update

func (t *Tester) Update(
	id string, payload interface{}) *Case

Create Case to Update something of the id with the payload

type Validator

type Validator func(interface{}) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL