Comments (15)
Here is more simple solution using net/http for test requests.
I hope this will save someone time.
// serve serves http request using provided fasthttp handler
func serve(handler fasthttp.RequestHandler, req *http.Request) (*http.Response, error) {
ln := fasthttputil.NewInmemoryListener()
defer ln.Close()
go func() {
err := fasthttp.Serve(ln, handler)
if err != nil {
panic(fmt.Errorf("failed to serve: %v", err))
}
}()
client := http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return ln.Dial()
},
},
}
return client.Do(req)
}
// Example usage
func TestHandler(t *testing.T) {
r, err := http.NewRequest("POST", "http://test/", nil)
if err != nil {
t.Error(err)
}
res, err := serve(MyHandler, r)
if err != nil {
t.Error(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
fmt.Println(string(body))
}
from fasthttp.
func serve(handler fasthttp.RequestHandler, req *fasthttp.Request, res *fasthttp.Response) error {
ln := fasthttputil.NewInmemoryListener()
defer ln.Close()
go func() {
err := fasthttp.Serve(ln, handler)
if err != nil {
logger.Error(err)
panic(err)
}
}()
client := fasthttp.Client{
Dial: func(addr string) (net.Conn, error) {
return ln.Dial()
},
}
return client.Do(req, res)
}
func TestListAllSubscription(t *testing.T) {
req := fasthttp.AcquireRequest()
req.SetRequestURI("/uri") // task URI
req.Header.SetMethod("GET")
req.Header.Set("Authorization", "Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTMxNDg2ODYwMjAwMDAxNyIsImFjY2Vzc190b2tlbiI6IkVBQUduN3QySWVCd0JBT1BvRVFPbGRBb0VLYTZVaWVKRlpBeDJzdnJ3V0lkTm40OFhscGR5MnhPVjdQbmtqTHc5bXhqNmpxSHZkaE9qdjhLNGtMdkRnRjdyRHZaQ2I0TmMwNXZHQlFUQkgxa3ozN3FMRXVVemdaQk9QMDNYWkFnR3dXcDE5bFhROEVpWkJYaW1aQVpCTzNYVVNwdno5Nm9GQmVmT3VUQnFnZmlMQVpEWkQifQ.L9WfIAS67TYESg4k-wanTV0gOJGEPEPG2ixIgPrCb68")
req.Header.SetContentType("application/json")
resp := fasthttp.AcquireResponse()
err := serve(handler, req, resp)
if err != nil {
}
logger.Info("resp from Postman.Post: ", resp)
logger.Info("resp status code", resp.StatusCode())
logger.Info("resp body", string(resp.Body()))
}
func handler(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(fasthttp.StatusOK)
}
from fasthttp.
@Annonomus-Penguin , there are many approaches:
1. Use distinct TCP port for each test:
func TestFoo(t *testing.T) {
port := 1234
defer startServerOnPort(t, port, requestHandler).Close()
// your tests here for client connecting to the given port
}
func TestBar(t *testing.T) {
port := 1235 // note - the port differs from TestFoo.
defer startServerOnPort(t, port, requestHandler).Close()
// your tests here for client connecting to the given port
}
func startServerOnPort(t *testing.T, port int, h fasthttp.RequestHandler) io.Closer {
ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
if err != nil {
t.Fatalf("cannot start tcp server on port %d: %s", port, err)
}
go fasthttp.Serve(ln, h)
return ln
}
2. Use unix sockets instead of TCP sockets. It is advisable to use distinct unix socket addresses (file paths) for each test:
func TestBar(t *testing.T) {
filepath := "/tmp/TestBar.sock"
defer startUnixServer(t, filepath, requestHandler).Close()
// now you must override Client.Transport.Dial for connecting to filepath.
// see http://stackoverflow.com/a/26224019/274937 for details.
}
func startUnixServer(t *testing.T, filepath string, h fasthttp.RequestHandler) io.Closer {
ln, err := net.Listen("unix", filepath)
if err != nil {
t.Fatalf("cannot start unix server on %q: %s", filepath, err)
}
go fasthttp.Serve(ln, h)
return ln
}
3. Create custom net.Listener
, which also implements Transport.Dial
and use net.Listener
part in server and Transport.Dial
part in client. I didn't test the following code, but you must get the idea:
type listenerDialer struct {
// server conns to accept
conns chan net.Conn
}
func NewListenerDialer() *listenerDialer {
ld := &listenerDialer{
conns: make(chan net.Conn),
}
return ld
}
// net.Listener interface implementation
func (ld *listenerDialer) Accept() (net.Addr, error) {
conn, ok := <-ld.conns
if !ok {
return nil, errors.New("listenerDialer is closed")
}
return conn, nil
}
// net.Listener interface implementation
func (ld *listenerDialer) Close() error {
close(ld.conns)
return nil
}
// net.Listener interface implementation
func (ld *listenerDialer) Addr() net.Addr {
// return arbitrary fake addr.
return &net.UnixAddr{
Name: "listenerDialer",
Net: "fake",
}
}
// Transport.Dial implementation
func (ld *listenerDialer) Dial(network, addr string) (net.Conn, error) {
cConn, sConn := net.Pipe()
ld.conns <- sConn
return cConn, nil
}
IMHO, the last approach is the best, since it doesn't limit you with "distinct TCP ports and/or unix socket file paths per test" rule.
from fasthttp.
@Annonomus-Penguin , just added InmemoryListener, which implements the last approach from my comment above.
from fasthttp.
hello, i want ask, How to do unit testing if the url have url parameters
like /:id
?
from fasthttp.
Coming late to the party, but using @mithleshmeghwal I created helper functions like the one used in the below test:
func TestHealthcheck(t *testing.T) {
path := "/healthcheck"
app := NewApp()
resp, err := testutils.TestGet(app, path)
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode())
assert.Equal(t, "WORKING", string(resp.Body()))
}
The testutils package includes such methods (TestGet
) and is described below:
package testutils
import (
"net"
routing "github.com/qiangxue/fasthttp-routing"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttputil"
)
func serve(app TestApplication, req *fasthttp.Request, res *fasthttp.Response) error {
ln := fasthttputil.NewInmemoryListener()
router := app.GetRouter()
defer ln.Close()
go func() {
err := fasthttp.Serve(ln, router.HandleRequest)
if err != nil {
panic(err)
}
}()
client := fasthttp.Client{
Dial: func(addr string) (net.Conn, error) {
return ln.Dial()
},
}
return client.Do(req, res)
}
//TestApplication interface
type TestApplication interface {
GetRouter() *routing.Router
}
//TestGet function - Simple to add headers and stuff later on, or you could receive a req object
func TestGet(app TestApplication, path string) (*fasthttp.Response, error) {
req := fasthttp.AcquireRequest()
req.SetRequestURI(path) // task URI
req.Header.SetMethod("GET")
req.Header.Set("Host", "localhost")
req.Header.SetContentType("application/json")
resp := fasthttp.AcquireResponse()
err := serve(app, req, resp)
return resp, err
}
Should be fairly simple to remove the TestApplication
interface if you want but I like the abstraction.
from fasthttp.
Dropping HTTP Server Tests article as a reference by @mstrYoda
from fasthttp.
Here is more simple solution using net/http for test requests.
I hope this will save someone time.// serve serves http request using provided fasthttp handler func serve(handler fasthttp.RequestHandler, req *http.Request) (*http.Response, error) { ln := fasthttputil.NewInmemoryListener() defer ln.Close() go func() { err := fasthttp.Serve(ln, handler) if err != nil { panic(fmt.Errorf("failed to serve: %v", err)) } }() client := http.Client{ Transport: &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return ln.Dial() }, }, } return client.Do(req) } // Example usage func TestHandler(t *testing.T) { r, err := http.NewRequest("POST", "http://test/", nil) if err != nil { t.Error(err) } res, err := serve(MyHandler, r) if err != nil { t.Error(err) } body, err := ioutil.ReadAll(res.Body) if err != nil { t.Error(err) } fmt.Println(string(body)) }
This worked like charm. Thanks!
from fasthttp.
If there's not an elegant solution, I've started messing around with mocking up an object that would be a fairly decent replacement.
from fasthttp.
so useful! thanks!
from fasthttp.
Came across this closed issue, but I wanted to contribute my solution. I was able to steal^H^H^H^H^Hleverage some code found in server_test.go to build a handler that my individual unit tests can call. Feel free to adapt to your needs.
// First, implement a ReadWriter and necessary methods
type readWriter struct {
net.Conn
r bytes.Buffer
w bytes.Buffer
}
func (rw *readWriter) Close() error {
return nil
}
func (rw *readWriter) Read(b []byte) (int, error) {
return rw.r.Read(b)
}
func (rw *readWriter) Write(b []byte) (int, error) {
return rw.w.Write(b)
}
func (rw *readWriter) RemoteAddr() net.Addr {
return zeroTCPAddr
}
func (rw *readWriter) LocalAddr() net.Addr {
return zeroTCPAddr
}
func (rw *readWriter) SetReadDeadline(t time.Time) error {
return nil
}
func (rw *readWriter) SetWriteDeadline(t time.Time) error {
return nil
}
// Currently I'm only looking at Status code and the response body. Feel free to add a header item.
type testHttpResponse struct {
code int
body []byte
}
// HTTPTestHandler - URL path as arg, returns testHttpResponse struct (code and response body).
func HTTPTestHandler(t *testing.T, path string, timeout int) (*testHttpResponse, error) {
s := &fasthttp.Server{
Handler: router,
}
// default timeout is 10s
if timeout == 0 {
timeout = 10
}
requestString := fmt.Sprintf("GET %v HTTP/1.1\r\nHost: localhost\r\n\r\n", path)
rw := &readWriter{}
rw.r.WriteString(requestString)
// Later on in this function we'll be calling http.ReadResponse to parse the raw HTTP output.
// http.ReadResponse requires a http.Request object as an arg, so we'll create one here.
req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader([]byte(requestString))))
ch := make(chan error)
go func() {
ch <- s.ServeConn(rw)
}()
select {
case err := <-ch:
if err != nil {
t.Fatalf("Unexpected error from serveConn: %s", err)
}
case <-time.After(timeout * time.Second):
t.Errorf("timeout")
}
resp, err := ioutil.ReadAll(&rw.w)
if err != nil {
t.Fatalf("Unexpected error from ReadAll: %s", err)
}
// And parse the returning text to a http.Response object
httpResp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(resp)), req)
code := httpResp.StatusCode
body, err := ioutil.ReadAll(httpResp.Body)
if err != nil {
t.Fatalf("Error reading HTTP response body")
}
return &testHttpResponse{code, body}, nil
}
// Test_healthCheckURL - Sample test leveraging HTTPTestHandler()
func Test_healthCheckURL(t *testing.T) {
url := "/healthcheck"
// Verify the status code is what we expect.
resp, err := HTTPTestHandler(t, url)
if err != nil {
t.Fatal(err)
}
// Check response code and body
assert.Equal(t, http.StatusOK, resp.code)
assert.Equal(t, "OK", string(resp.body))
}
from fasthttp.
@holykol Your code is missing the declaration for MyHandler, can you update?
from fasthttp.
@cwoodfield
Sorry, MyHandler
is unclear. This is actually your handler that you want to test. For example:
func MyHandler(ctx *fasthttp.RequestCtx) { // Actually yours
fmt.Fprint(ctx, "It's working!")
}
from fasthttp.
@holykol is it ok to use http.NewRequest with fasthttp and func serve(handler fasthttp.RequestHandler, req *http.Request) (*http.Response, error) {
for testing?
from fasthttp.
@holykol @mithleshmeghwal
Thanks for the examples. :) I tried to implement custom Handler()
method that return should custom status and body. But somehow, I couldn't make it work as i expected.
func MyHandler(handler fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
handler(ctx)
fmt.Fprintf(ctx, "URI: %q", ctx.RequestURI())
ctx.Response.ResetBody()
ctx.Response.SetStatusCode(fasthttp.StatusBadGateway)
ctx.Response.SetBodyString("MY TEST BODY")
}
}
I'm calling this body in this way:
h := fasthttp.FSHandler("/test", 0)
h = MyHandler(h)
_ = serve(h, req, res)
But still, even when these set, i'm getting 200
and null
body:
res status: 200
res body: []
@valyala I'm trying to create a custom mock api server which returns custom static json bodies per endpoint. For example, I want to send 100 different mock body data for 100 endpoints with using NewInmemoryListener()
function, however, i do not want to call serve
function as much as the endpoints I use, singleton server pattern should be fine i guess.
If i can not do it this way; as described here, i can create a mock function for Do
:
type MockClient struct {
DoFunc func(req *http.Request) (*http.Response, error)
}
What do you recommend?
from fasthttp.
Related Issues (20)
- [BUG?]: SSE Handler with Fiber web framework, any http client I have implemented cannot connect.
- Question: Add client.MaxResponseBody*Read*Size? HOT 2
- Bug: fasthttp client connect to Vite dev server on localhost, tcp4 127.0.0.1:5173: connect: connection refused HOT 2
- Support for Prometheus HOT 1
- CloseOnShutdown dont work? HOT 3
- how can i use fasthttp bare minimum "net" library only without routing through the http overhead? just pure tcp rpc stuff will do HOT 1
- What is a correct way to create a proper fasthttp.RequestCtx in the unit tests? HOT 1
- Timeout stream response connection does not clear buffer data for re-use HOT 2
- Propagate request error to RetryIfFunc HOT 1
- Expose `timeout` field in Request HOT 1
- NewFastHTTPHandler does not set response status code. HOT 1
- NewFastHTTPHandler does not set response status code HOT 7
- Error when serving connection "x.x.x.x:443"<->"172.56.198.126:18271": EOF HOT 3
- http parsing of the request line - where does that happen? HOT 1
- gnet is going to have tls working on it very soon. was wondering if anyone has tried to port fasthttp to use gnet instead. HOT 1
- Feature request : update and show all examples for doing zero allocation when using fasthttp. 100% zero alloc for each req / resp. HOT 3
- It is not safe to read all stream body to memory without a max size limit. HOT 1
- serving a compress enabled public folder without +w permissions results in a 404 HOT 1
- no free connections available to host HOT 3
- when setting MaxConnsPerIP to value greater than zero, the TLSConnectionState( ) is null on a TLS connection returned from the worker pool. HOT 11
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fasthttp.