Hello World Golang server application

gin-gonic framework

Api Server

mkdir go-api
cd go-api

# Initialize golang module
go mod init go-api

# Install gin-gonic package
go get -u github.com/gin-gonic/gin

touch main.go
// main.go
package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    // listen and serve on 0.0.0.0:8080
    r.Run()
}
# Start api server
go run .

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:    export GIN_MODE=release
 - using code:    gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080


# Test
curl localhost:8080/ping
{"message":"pong"}⏎

Testing

Unit Testing

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestPingRoute(t *testing.T) {
    r := setupRouter()

    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/ping", nil)
    r.ServeHTTP(w, req)

    if w.Code != 200 {
        t.Errorf("Expected status code 200, but got %d", w.Code)
    }

    expected := `{"message":"pong"}`
    if w.Body.String() != expected {
        t.Errorf("Expected body to be %s, but got %s", expected, w.Body.String())
    }
}
# Execute tests
❯ go test .
ok      go-api    0.317s

Benchmark Testing


# Execute tests in benchmark mode
❯ go test -bench=. -count 2
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:    export GIN_MODE=release
 - using code:    gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> go-api.setupRouter.func1 (3 handlers)
[GIN] 2024/03/19 - 21:21:17 | 200 |      46.125µs |                 | GET      "/ping"
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:    export GIN_MODE=release
 - using code:    gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> go-api.setupRouter.func1 (3 handlers)
[GIN] 2024/03/19 - 21:21:17 | 200 |       2.375µs |                 | GET      "/ping"
PASS
ok      go-api    0.132s

Performance Testing

Pre-requiresite

Test script

mkdir tests/performance
touch tests/performance/main.js
// main.js
import http from 'k6/http';
import { check } from 'k6';

export default function () {
  const res = http.get('http://localhost:8080/ping');
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
}
  • Start api server in window 1 - go run .

  • Run performance tests


❯ k6 run tests/performance/main.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  (‾)  |
  / __________ \  |__| \__\ \_____/ .io

     execution: local
        script: tests/performance/main.js
        output: -

     scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
              * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)


     ✓ is status 200

     checks.........................: 100.00% ✓ 1          ✗ 0
     data_received..................: 141 B   82 kB/s
     data_sent......................: 84 B    49 kB/s
     http_req_blocked...............: avg=839µs  min=839µs  med=839µs  max=839µs  p(90)=839µs  p(95)=839µs
     http_req_connecting............: avg=256µs  min=256µs  med=256µs  max=256µs  p(90)=256µs  p(95)=256µs
     http_req_duration..............: avg=708µs  min=708µs  med=708µs  max=708µs  p(90)=708µs  p(95)=708µs
       { expected_response:true }...: avg=708µs  min=708µs  med=708µs  max=708µs  p(90)=708µs  p(95)=708µs
     http_req_failed................: 0.00%   ✓ 0          ✗ 1
     http_req_receiving.............: avg=37µs   min=37µs   med=37µs   max=37µs   p(90)=37µs   p(95)=37µs
     http_req_sending...............: avg=53µs   min=53µs   med=53µs   max=53µs   p(90)=53µs   p(95)=53µs
     http_req_tls_handshaking.......: avg=0s     min=0s     med=0s     max=0s     p(90)=0s     p(95)=0s
     http_req_waiting...............: avg=618µs  min=618µs  med=618µs  max=618µs  p(90)=618µs  p(95)=618µs
     http_reqs......................: 1       581.733566/s
     iteration_duration.............: avg=1.66ms min=1.66ms med=1.66ms max=1.66ms p(90)=1.66ms p(95)=1.66ms
     iterations.....................: 1       581.733566/s


running (00m00.0s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m00.0s/10m0s  1/1 iters, 1 per VU