-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How do you handle unit tests? #36
Comments
If there's not an elegant solution, I've started messing around with mocking up an object that would be a fairly decent replacement. |
@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
|
…nt<->server tests and fast in-process client<->server communication
@Annonomus-Penguin , just added InmemoryListener, which implements the last approach from my comment above. |
so useful! thanks! |
Support the same redirect status codes as the golang standard library does: https://github.com/golang/go/blob/15095be9fbe726d4a3ef43b68d2fbc83e6484ded/src/net/http/client.go#L419 Fixes valyala#36 and valyala#338
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))
} |
Here is more simple solution using net/http for test requests. // 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))
} |
@holykol Your code is missing the declaration for MyHandler, can you update? |
@cwoodfield func MyHandler(ctx *fasthttp.RequestCtx) { // Actually yours
fmt.Fprint(ctx, "It's working!")
} |
@holykol is it ok to use http.NewRequest with fasthttp and |
|
hello, i want ask, How to do unit testing if the url have |
@holykol @mithleshmeghwal Thanks for the examples. :) I tried to implement custom
I'm calling this body in this way:
But still, even when these set, i'm getting
@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 If i can not do it this way; as described here, i can create a mock function for
What do you recommend? |
This worked like charm. Thanks! |
Coming late to the party, but using @mithleshmeghwal I created helper functions like the one used in the below test:
The testutils package includes such methods (
Should be fairly simple to remove the |
Dropping HTTP Server Tests article as a reference by @mstrYoda |
I've been exploring full-stack unit testing for apps built on fasthttp, and my initial instinct was to use the default http package (to reduce the chance of a fasthttp flaw being shared between client and server, causing something to go undetected). However, Go doesn't like sharing a TCP port:
You seem to use unexported functions to do internal testing. What's the recommended way to do testing without running into errors with these? I'm doing a
server.ListenAndServe(":8080")
for the server and then doing this to connect to the localhost server:I've been considering mocking up the net.Listener interface and passing that to
Server.Serve()
. Is there a better solution? What do you suggest?The text was updated successfully, but these errors were encountered: