Echo API Testing
Testing is a critical part of API development that ensures your endpoints function correctly and reliably. This guide will walk you through various approaches to testing Echo APIs, from simple manual tests to automated testing frameworks.
Introduction to API Testing
API testing involves sending requests to your API endpoints and verifying that the responses match your expectations. For an Echo API, we need to validate:
- Response status codes
- Response body content
- Headers
- Performance metrics
- Error handling
Good testing practices help catch bugs early, ensure consistent behavior, and provide confidence when refactoring or adding new features.
Manual Testing Tools
Using curl
curl
is a command-line tool for transferring data with URLs. It's perfect for quick API tests:
# Testing a GET endpoint
curl http://localhost:1323/users
# Testing a POST endpoint with JSON data
curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "email": "[email protected]"}' http://localhost:1323/users
Using Postman
Postman provides a graphical interface for API testing:
- Create a new request
- Select the HTTP method (GET, POST, PUT, DELETE)
- Enter your URL (e.g.,
http://localhost:1323/users
) - Add headers if needed
- Add request body for POST/PUT requests
- Send the request and examine the response
Postman also allows you to save requests, create collections, and even automate tests with scripts.
Automated Testing in Go
Unit Testing Echo Handlers
Echo makes it easy to write unit tests for your handlers using Go's built-in testing package and Echo's test utilities.
Here's a basic example of testing a simple Echo handler:
package handlers
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func TestHelloHandler(t *testing.T) {
// Setup
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/hello", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Assertions
if assert.NoError(t, HelloHandler(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "Hello, World!", rec.Body.String())
}
}
func TestUserCreateHandler(t *testing.T) {
// Setup
e := echo.New()
jsonBody := `{"name":"John Doe","email":"[email protected]"}`
req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(jsonBody))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Assertions
if assert.NoError(t, CreateUserHandler(c)) {
assert.Equal(t, http.StatusCreated, rec.Code)
assert.Contains(t, rec.Body.String(), "John Doe")
assert.Contains(t, rec.Body.String(), "[email protected]")
}
}
Integration Testing
Integration tests verify that your API works correctly with all components connected. Here's how to write an integration test for an Echo API:
func TestUserAPIIntegration(t *testing.T) {
// Setup server
e := setupEchoServer() // Your function that configures the Echo server
server := httptest.NewServer(e)
defer server.Close()
// Test GET users (initially empty)
resp, err := http.Get(server.URL + "/users")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Test POST to create a user
userJSON := `{"name":"Jane Smith","email":"[email protected]"}`
resp, err = http.Post(
server.URL+"/users",
"application/json",
strings.NewReader(userJSON),
)
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, resp.StatusCode)
// Test GET users again (should contain the new user)
resp, err = http.Get(server.URL + "/users")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.Contains(t, string(body), "Jane Smith")
}
Testing Best Practices
1. Test Request Validation
Echo provides built-in request validation. Test that your API properly validates input data:
func TestInvalidUserData(t *testing.T) {
e := echo.New()
e.Validator = setupValidator() // Your validator setup
// Invalid email
jsonBody := `{"name":"John Doe","email":"not-an-email"}`
req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(jsonBody))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Call handler
CreateUserHandler(c)
// Should return validation error
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "validation failed")
}
2. Test Error Cases
Always test how your API handles errors:
func TestUserNotFound(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/users/:id")
c.SetParamNames("id")
c.SetParamValues("999") // Non-existent ID
// Call handler
GetUserHandler(c)
// Should return not found
assert.Equal(t, http.StatusNotFound, rec.Code)
}
3. Test Middleware
Test that middleware functions correctly:
func TestAuthMiddleware(t *testing.T) {
e := echo.New()
// Test with no auth token
req := httptest.NewRequest(http.MethodGet, "/protected", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
h := AuthMiddleware()(func(c echo.Context) error {
return c.String(http.StatusOK, "You're in!")
})
// Should return unauthorized
h(c)
assert.Equal(t, http.StatusUnauthorized, rec.Code)
// Test with valid auth token
req = httptest.NewRequest(http.MethodGet, "/protected", nil)
req.Header.Set("Authorization", "Bearer valid-token")
rec = httptest.NewRecorder()
c = e.NewContext(req, rec)
h(c)
assert.Equal(t, http.StatusOK, rec.Code)
}
Testing HTTP Response Codes
Make sure your API returns appropriate HTTP status codes:
Scenario | Expected Status Code |
---|---|
Successful GET | 200 OK |
Successful POST | 201 Created |
Successful DELETE | 204 No Content |
Bad Request | 400 Bad Request |
Unauthorized | 401 Unauthorized |
Forbidden | 403 Forbidden |
Not Found | 404 Not Found |
Server Error | 500 Internal Server Error |