Testing API Handler written in Golang.

Gofight

GoDoc Build Status Go Report Card codebeat badge codecov Sourcegraph

API Handler Testing for Golang Web framework.

Support Framework

Usage

Download this package.

$ go get github.com/appleboy/gofight/v2

To import this package, add the following line to your code:

import "github.com/appleboy/gofight/v2"

Usage

The following is basic testing example.

Main Program:

package main

import (
	"io"
	"net/http"
)

func BasicHelloHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Hello World")
}

func BasicEngine() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/", BasicHelloHandler)

	return mux
}

Testing:

package main

import (
	"net/http"
	"testing"

	"github.com/appleboy/gofight/v2"
	"github.com/stretchr/testify/assert"
)

func TestBasicHelloWorld(t *testing.T) {
	r := gofight.New()

	r.GET("/").
		// turn on the debug mode.
		SetDebug(true).
		Run(BasicEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {

			assert.Equal(t, "Hello World", r.Body.String())
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

Set Header

You can add custom header via SetHeader func.

func TestBasicHelloWorld(t *testing.T) {
	r := gofight.New()
	version := "0.0.1"

	r.GET("/").
		// turn on the debug mode.
		SetDebug(true).
		SetHeader(gofight.H{
			"X-Version": version,
		}).
		Run(BasicEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {

			assert.Equal(t, version, rq.Header.Get("X-Version"))
			assert.Equal(t, "Hello World", r.Body.String())
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

POST FORM Data

Using SetForm to generate form data.

func TestPostFormData(t *testing.T) {
	r := gofight.New()

	r.POST("/form").
		SetForm(gofight.H{
			"a": "1",
			"b": "2",
		}).
		Run(BasicEngine(), func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			a, _ := jsonparser.GetString(data, "a")
			b, _ := jsonparser.GetString(data, "b")

			assert.Equal(t, "1", a)
			assert.Equal(t, "2", b)
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

POST JSON Data

Using SetJSON to generate JSON data.

func TestPostJSONData(t *testing.T) {
	r := gofight.New()

	r.POST("/json").
		SetJSON(gofight.D{
			"a": 1,
			"b": 2,
		}).
		Run(BasicEngine, func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			a, _ := jsonparser.GetInt(data, "a")
			b, _ := jsonparser.GetInt(data, "b")

			assert.Equal(t, 1, int(a))
			assert.Equal(t, 2, int(b))
			assert.Equal(t, http.StatusOK, r.Code)
			assert.Equal(t, "application/json; charset=utf-8", r.HeaderMap.Get("Content-Type"))
		})
}

POST RAW Data

Using SetBody to generate raw data.

func TestPostRawData(t *testing.T) {
	r := gofight.New()

	r.POST("/raw").
		SetBody("a=1&b=1").
		Run(BasicEngine, func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			a, _ := jsonparser.GetString(data, "a")
			b, _ := jsonparser.GetString(data, "b")

			assert.Equal(t, "1", a)
			assert.Equal(t, "2", b)
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

Set Query String

Using SetQuery to generate raw data.

func TestQueryString(t *testing.T) {
	r := gofight.New()

	r.GET("/hello").
		SetQuery(gofight.H{
			"a": "1",
			"b": "2",
		}).
		Run(BasicEngine, func(r HTTPResponse, rq HTTPRequest) {
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

or append exist query parameter.

func TestQueryString(t *testing.T) {
	r := gofight.New()

	r.GET("/hello?foo=bar").
		SetQuery(gofight.H{
			"a": "1",
			"b": "2",
		}).
		Run(BasicEngine, func(r HTTPResponse, rq HTTPRequest) {
			assert.Equal(t, http.StatusOK, r.Code)
		})
}

Set Cookie String

Using SetCookie to generate raw data.

func TestQueryString(t *testing.T) {
	r := gofight.New()

	r.GET("/hello").
		SetCookie(gofight.H{
			"foo": "bar",
		}).
		Run(BasicEngine, func(r HTTPResponse, rq HTTPRequest) {
			assert.Equal(t, http.StatusOK, r.Code)
			assert.Equal(t, "foo=bar", rq.Header.Get("cookie"))
		})
}

Set JSON Struct

type User struct {
	// Username user name
	Username string `json:"username"`
	// Password account password
	Password string `json:"password"`
}

func TestSetJSONInterface(t *testing.T) {
	r := New()

	r.POST("/user").
		SetJSONInterface(User{
			Username: "foo",
			Password: "bar",
		}).
		Run(framework.GinEngine(), func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			username := gjson.GetBytes(data, "username")
			password := gjson.GetBytes(data, "password")

			assert.Equal(t, "foo", username.String())
			assert.Equal(t, "bar", password.String())
			assert.Equal(t, http.StatusOK, r.Code)
			assert.Equal(t, "application/json; charset=utf-8", r.HeaderMap.Get("Content-Type"))
		})
}

Upload multiple file with absolute path and parameter

The following is route using gin

func gintFileUploadHandler(c *gin.Context) {
	ip := c.ClientIP()
	hello, err := c.FormFile("hello")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	helloFile, _ := hello.Open()
	helloBytes := make([]byte, 6)
	helloFile.Read(helloBytes)

	world, err := c.FormFile("world")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	worldFile, _ := world.Open()
	worldBytes := make([]byte, 6)
	worldFile.Read(worldBytes)

	foo := c.PostForm("foo")
	bar := c.PostForm("bar")
	c.JSON(http.StatusOK, gin.H{
		"hello":		 hello.Filename,
		"world":		 world.Filename,
		"foo":			 foo,
		"bar":			 bar,
		"ip":				ip,
		"helloSize": string(helloBytes),
		"worldSize": string(worldBytes),
	})
}

Write the testing:

func TestUploadFile(t *testing.T) {
	r := New()

	r.POST("/upload").
		SetDebug(true).
		SetFileFromPath([]UploadFile{
			{
				Path: "./testdata/hello.txt",
				Name: "hello",
			},
			{
				Path: "./testdata/world.txt",
				Name: "world",
			},
		}, H{
			"foo": "bar",
			"bar": "foo",
		}).
		Run(framework.GinEngine(), func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			hello := gjson.GetBytes(data, "hello")
			world := gjson.GetBytes(data, "world")
			foo := gjson.GetBytes(data, "foo")
			bar := gjson.GetBytes(data, "bar")
			ip := gjson.GetBytes(data, "ip")
			helloSize := gjson.GetBytes(data, "helloSize")
			worldSize := gjson.GetBytes(data, "worldSize")

			assert.Equal(t, "world\n", helloSize.String())
			assert.Equal(t, "hello\n", worldSize.String())
			assert.Equal(t, "hello.txt", hello.String())
			assert.Equal(t, "world.txt", world.String())
			assert.Equal(t, "bar", foo.String())
			assert.Equal(t, "foo", bar.String())
			assert.Equal(t, "", ip.String())
			assert.Equal(t, http.StatusOK, r.Code)
			assert.Equal(t, "application/json; charset=utf-8", r.HeaderMap.Get("Content-Type"))
		})
}

Upload multiple file with content []byte path and parameter

func TestUploadFileByContent(t *testing.T) {
	r := New()

	helloContent, err := ioutil.ReadFile("./testdata/hello.txt")
	if err != nil {
		log.Fatal(err)
	}

	worldContent, err := ioutil.ReadFile("./testdata/world.txt")
	if err != nil {
		log.Fatal(err)
	}

	r.POST("/upload").
		SetDebug(true).
		SetFileFromPath([]UploadFile{
			{
				Path:		"hello.txt",
				Name:		"hello",
				Content: helloContent,
			},
			{
				Path:		"world.txt",
				Name:		"world",
				Content: worldContent,
			},
		}, H{
			"foo": "bar",
			"bar": "foo",
		}).
		Run(framework.GinEngine(), func(r HTTPResponse, rq HTTPRequest) {
			data := []byte(r.Body.String())

			hello := gjson.GetBytes(data, "hello")
			world := gjson.GetBytes(data, "world")
			foo := gjson.GetBytes(data, "foo")
			bar := gjson.GetBytes(data, "bar")
			ip := gjson.GetBytes(data, "ip")
			helloSize := gjson.GetBytes(data, "helloSize")
			worldSize := gjson.GetBytes(data, "worldSize")

			assert.Equal(t, "world\n", helloSize.String())
			assert.Equal(t, "hello\n", worldSize.String())
			assert.Equal(t, "hello.txt", hello.String())
			assert.Equal(t, "world.txt", world.String())
			assert.Equal(t, "bar", foo.String())
			assert.Equal(t, "foo", bar.String())
			assert.Equal(t, "", ip.String())
			assert.Equal(t, http.StatusOK, r.Code)
			assert.Equal(t, "application/json; charset=utf-8", r.HeaderMap.Get("Content-Type"))
		})
}

Example

License

Copyright 2019 Bo-Yi Wu @appleboy.

Licensed under the MIT License.

Owner
Bo-Yi Wu
I really believe committing every day on an open source project is the best practice.
Bo-Yi Wu
Comments
  • Doesn't support http.DefaultServeMux?

    Doesn't support http.DefaultServeMux?

    main.go

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "os"
    )
    
    func helloHandler(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello World")
    }
    
    func main() {
        http.HandleFunc("/hello", helloHandler)
        if err := http.ListenAndServe(":2016", nil); err != nil {
            fmt.Println(nil, "http listen at 2016 failed. err is", err)
            os.Exit(-1)
        }
    }
    

    main_test.go

    package main
    
    import (
        "net/http"
        "testing"
    
        "github.com/appleboy/gofight"
        "github.com/stretchr/testify/assert"
    )
    
    func TestHello(t *testing.T) {
        r := gofight.New()
    
        r.GET("/hello").
            SetDebug(true).
            Run(http.DefaultServeMux, func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
                assert.Equal(t, http.StatusOK, r.Code)
            })
    }
    
    
    ❯ go test
    2016/04/19 11:46:13 Request Method: GET
    2016/04/19 11:46:13 Request Path: /hello
    2016/04/19 11:46:13 Request Body:
    2016/04/19 11:46:13 Request Headers: map[]
    2016/04/19 11:46:13 Request Header: map[User-Agent:[Gofight-client/1.0]]
    --- FAIL: TestHello (0.00s)
            Error Trace:    main_test.go:17
                            gofight.go:269
                            main_test.go:18
            Error:          Not equal: 200 (expected)
                                    != 404 (actual)
    
    FAIL
    exit status 1
    FAIL    _/Users/akagi201/Documents/learning-golang/src/gofight/default-serve-mux        0.011s
    
  • Test error for glide install.

    Test error for glide install.

    # github.com/appleboy/gofight/vendor/github.com/labstack/echo
    ../vendor/github.com/labstack/echo/echo.go:245: cannot use "github.com/appleboy/gofight/vendor/github.com/labstack/gommon/log".New("echo
    ") (type *"github.com/appleboy/gofight/vendor/github.com/labstack/gommon/log".Logger) as type Logger in field value:
            *"github.com/appleboy/gofight/vendor/github.com/labstack/gommon/log".Logger does not implement Logger (missing Panic method)
    ../vendor/github.com/labstack/echo/echo.go:558: e.Color.Printf undefined (type *color.Color has no field or method Printf)
    ../vendor/github.com/labstack/echo/echo.go:562: e.Color.Printf undefined (type *color.Color has no field or method Printf)
    FAIL    github.com/appleboy/gofight/example [build failed]
    make: *** [example] Error 2
    

    as title.

  • Support echo v3.0.0

    Support echo v3.0.0

    Fixed #38 Fixed #39

    We don't need initEchoTest and RunEcho function now.

    I will bump to v2.0.0 to support echo v3.0.0 version and v1.x support echo under v2.x version.

    also fix https://github.com/labstack/echo/issues/439

    cc @mdouchement @shawnzhu

  • Problem with go module

    Problem with go module

    Hello,

    I have a problem when using go module ...

    go: github.com/appleboy/[email protected]+incompatible: go.mod has post-v2 module path "github.com/appleboy/gofight/v2" at revision v2.0.0
    go: gopkg.in/appleboy/[email protected]: go.mod has non-....v2 module path "github.com/appleboy/gofight/v2" at revision v2.0.0
    go: error loading module requirements
    

    Thanks.

  • How do I set an array of JSON?

    How do I set an array of JSON?

    https://s.natalian.org/2018-06-20/setbody.mp4

    Puzzled how to set a JSON structure like: [ "Ford", "BMW", "Fiat" ]. Do I need to use https://godoc.org/github.com/appleboy/gofight#RequestConfig.SetBody in this case?

  • How to handle Parameters in path

    How to handle Parameters in path

    How we can handle parameters in path like I want to set name /user/:name here When I send a request to /user/reza I get a 404 error and when I sent request to "/user" I get 400 because I did not provide the path parameter.

  • Mention tango in the README?

    Mention tango in the README?

    Hi,

    While packaging this software for Debian, I was drafting short/long descriptions and checking the supported frameworks, it seems Tango could also be mentioned as supported?

    Cheers, Cyril.

  • Incompatible with Go 1.13

    Incompatible with Go 1.13

    Hi guys,

    the module is not compatible with go 1.13.

    go list -m -json all
    go: gopkg.in/appleboy/[email protected]: go.mod has non-....v2 module path "github.com/appleboy/gofight/v2" at revision v2.0.0
    

    I talked to IntelliJ support and they confirmed it.

    It's the same as https://github.com/appleboy/gofight/issues/74.

    Please look into it guys, thank you.

    Kind regards b0rski

  • feat(file): support upload single file.

    feat(file): support upload single file.

    fix #55

    See the example:

    func TestUploadFile(t *testing.T) {
    	r := New()
    
    	r.POST("/upload").
    		SetFileFromPath("fixtures/hello.txt", "test", H{
    			"foo": "bar",
    			"bar": "foo",
    		}).
    		Run(framework.GinEngine(), func(r HTTPResponse, rq HTTPRequest) {
    			data := []byte(r.Body.String())
    
    			hello := gjson.GetBytes(data, "hello")
    			filename := gjson.GetBytes(data, "filename")
    			foo := gjson.GetBytes(data, "foo")
    			bar := gjson.GetBytes(data, "bar")
    
    			assert.Equal(t, "world", hello.String())
    			assert.Equal(t, "hello.txt", filename.String())
    			assert.Equal(t, "bar", foo.String())
    			assert.Equal(t, "foo", bar.String())
    			assert.Equal(t, http.StatusOK, r.Code)
    			assert.Equal(t, "application/json; charset=utf-8", r.HeaderMap.Get("Content-Type"))
    		})
    }
    
  • Switch off escaping HTML when serializing JSON

    Switch off escaping HTML when serializing JSON

    Hi It would be nice if we could disable html escaping

    	encoder := json.NewEncoder(w)
    	encoder.SetEscapeHTML(false)
    	encoder.Encode(r.Data)
    

    Cheers, Jan

  • Benchmark examples?

    Benchmark examples?

    I need to be able to test my endpoint can handle say 5 simultaneous requests.

    Does gofight + "*testing.B" make sense? I can't find an example. Or should I reach for another tool?

    https://github.com/search?q=%22*testing.B%22+%22github.com%2Fappleboy%2Fgofight%22&type=Code

  • go fight.HTTPResponse masking ResponseRecorder methods?

    go fight.HTTPResponse masking ResponseRecorder methods?

    I might be missing something totally obvious here. If so I apologize in advance.

    It seems like by aliasing *httptest.ResponseRecorder with HTTPResponse, you can no longer access the methods on the ResponseRecorder object. This means to access headers you must use the deprecated HeaderMap object, rather than calling the Header() method as is suggested.

    Is there a way to call the method on the type aliased object?

    It doesn't look like there's a huge amount of gain from aliasing the object outside saving a few keystrokes, so would it be possible to just use the actual object instead of the aliased one? I'd be happy to put a pull request together, though this would change the public interface of the project..

    Any guidance in this matter would be appreciated!!

    Thank you for making this library btw, it's made my life much nicer!

  • Set the IP address of the outgoing test request

    Set the IP address of the outgoing test request

    Hi, is there any way to set the IP address of the mock request? I have a use case where I need to know the IP address of the incoming request. The library sets the IP address to an empty string. I need to set it to a particular value (could default to 127.0.0.1). Any help would be much appreciated.

    Thanks

A yaml data-driven testing format together with golang testing library

Specimen Yaml-based data-driven testing Specimen is a yaml data format for data-driven testing. This enforces separation between feature being tested

Nov 24, 2022
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

Dec 12, 2022
Expressive end-to-end HTTP API testing made easy in Go

baloo Expressive and versatile end-to-end HTTP API testing made easy in Go (golang), built on top of gentleman HTTP client toolkit. Take a look to the

Dec 13, 2022
API testing framework inspired by frisby-js
API testing framework inspired by frisby-js

frisby REST API testing framework inspired by frisby-js, written in Go Proposals I'm starting to work on frisby again with the following ideas: Read s

Sep 27, 2022
End-to-end HTTP and REST API testing for Go.

httpexpect Concise, declarative, and easy to use end-to-end HTTP and REST API testing for Go (golang). Basically, httpexpect is a set of chainable bui

Jan 5, 2023
A Go library help testing your RESTful API application

RESTit A Go micro-framework to help writing RESTful API integration test Package RESTit provides helps to those who want to write an integration test

Oct 28, 2022
Markdown based document-driven RESTful API testing.
Markdown based document-driven RESTful API testing.

silk Markdown based document-driven web API testing. Write nice looking Markdown documentation (like this), and then run it using the silk command Sim

Dec 18, 2022
Hsuan-Fuzz: REST API Fuzzing by Coverage Level Guided Blackbox Testing
Hsuan-Fuzz: REST API Fuzzing by Coverage Level Guided Blackbox Testing

Hsuan-Fuzz: REST API Fuzzing by Coverage Level Guided Blackbox Testing Architecture Usage package main import ( restAPI "github.com/iasthc/hsuan-

Nov 30, 2022
mock server to aid testing the jaguar-java client API

stripe-mock stripe-mock is a mock HTTP server that responds like the real Stripe API. It can be used instead of Stripe's test mode to make test suites

Dec 24, 2021
Terminal application used for API testing
Terminal application used for API testing

Easily create, manage and execute http requests from the terminal.

Dec 20, 2022
Testy is a Go test running framework designed for Gametime's API testing needs.

template_library import "github.com/gametimesf/template_library" Overview Index Overview Package template_library is a template repository for buildin

Jun 21, 2022
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.

Fortio Fortio (Φορτίο) started as, and is, Istio's load testing tool and now graduated to be its own project. Fortio is also used by, among others, Me

Jan 2, 2023
Golang HTTP client testing framework

flute Golang HTTP client testing framework Presentation https://speakerdeck.com/szksh/flute-golang-http-client-testing-framework Overview flute is the

Sep 27, 2022
Extremely flexible golang deep comparison, extends the go testing package and tests HTTP APIs
Extremely flexible golang deep comparison, extends the go testing package and tests HTTP APIs

go-testdeep Extremely flexible golang deep comparison, extends the go testing package. Latest news Synopsis Description Installation Functions Availab

Dec 22, 2022
HTTP mock for Golang: record and replay HTTP/HTTPS interactions for offline testing

govcr A Word Of Warning I'm in the process of partly rewriting govcr to offer better support for cassette mutations. This is necessary because when I

Dec 28, 2022
Simple Golang Load testing app built on top of vegeta
Simple Golang Load testing app built on top of vegeta

LOVE AND WAR : Give Your App Love By Unleashing War Simple load testing app to test your http services Installation Build docker image: docker build -

Oct 26, 2021
Example basic fundamental testing in golang
Example basic fundamental testing in golang

Golang Fundamental Testing Example write basic fundamental testing in Golang Installation $ go mod download || go get . Implementation Case 01_testing

Jun 1, 2022
Gostresslib - A golang library for stress testing.

GoStressLib A golang library for stress testing. Install go get github.com/tenhan/gostresslib Usage package main import ( "github.com/tenhan/gostres

Nov 9, 2022
:exclamation:Basic Assertion Library used along side native go testing, with building blocks for custom assertions

Package assert Package assert is a Basic Assertion library used along side native go testing Installation Use go get. go get github.com/go-playground/

Jan 6, 2023