Proyecto base para aplicaciones Gin con ejemplos de configuración, testing y buenas prácticas.
- Instalación
- Variables de Entorno
- Ejecutar Aplicación
- Testing
- Tests de Aceptación
- Tests de Rendimiento
- Swagger
- Links de Referencia
Para instalar go debemos bajarlo e instalarlo de la siguiente pagina https://go.dev/doc/install
Para instalar los paquetes debemos ejecutar el siguiente comando
go mod tidyEste proyecto utiliza dotenv por lo que podemos crear el archivo .env con las siguientes variables
SECRET_KEY=sdadasffadf
JWT_SECRET_KEY=dsaadfdass
CORS_ORIGIN=*
Estas variables son las llaves secretas que se obtienen por variable de entorno, tambien tenemos el cors origin por si queremos dejarlo especifico para un dominio en particular
Se debe ejecutar el siguiente comando
go run main.goA continuacion dejo los comandos a utilizar para generar la imagen y posteriormente ejecutarla
Para generar la imagen debemos utilizar el siguiente comando
docker build -t go-gin .Para ejecutar la imagen debemos utilizar el siguiente comando
docker run -p 8080:8080 go-ginSe debe ejecutar el siguiente comando
go test ./repository ./client ./service ./controller ./routes ./configSe debe ejecutar el siguiente comando
go test -cover ./repository ./client ./service ./controller ./routes ./configSe deben ejecutar los siguientes comandos
go test -coverprofile="coverage.out" ./repository ./client ./service ./controller ./routes ./config
go tool cover -func="coverage.out"Se deben ejecutar los siguientes comandos
go test -coverprofile="coverage.out" ./repository ./client ./service ./controller ./routes ./config
go tool cover -html="coverage.out" -o coverage.htmlSe debe crear un archivo acceptance-test/main_test.go con el siguiente contenido
package main
import (
"os"
"testing"
"github.com/cucumber/godog"
)
func TestFeatures(t *testing.T) {
var outputFile *os.File
var err error
outputFile, err = os.Create("godog-report.json")
if err != nil {
t.Fatalf("failed to create report file: %v", err)
}
opts := godog.Options{
Format: "cucumber",
Paths: []string{"."},
Output: outputFile,
TestingT: t,
Strict: true,
}
godog.TestSuite{
Name: "acceptance",
ScenarioInitializer: InitializeScenario,
Options: &opts,
}.Run()
}Esto nos permite definir la ruta de los features, el archivo y formato de salida de reporte de los tests de aceptación
Se debe ejecutar el siguiente comando
go test ./acceptance-testAl finalizar generara un reporte godog-report.json
Se debe crear un archivo performance-test/vegeta/main_test.go con el siguiente contenido
package performance
import (
"os"
"testing"
"time"
vegeta "github.com/tsenart/vegeta/v12/lib"
)
var apiHost string = "http://localhost:8080/api"
func init() {
if os.Getenv("API_HOST") != "" {
apiHost = os.Getenv("API_HOST")
}
}
func TestProductEndpointPerformance(t *testing.T) {
rate := vegeta.Rate{Freq: 10, Per: time.Second} // 10 requests por segundo
duration := 5 * time.Second
endpoint := "products"
targeter := vegeta.NewStaticTargeter(vegeta.Target{
Method: "GET",
URL: apiHost + "/products",
})
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, duration, "GET /products") {
metrics.Add(res)
}
metrics.Close()
// Reporte en formato texto
textFile, err := os.Create("vegeta-" + endpoint + "-report.txt")
if err != nil {
t.Fatalf("failed to create text report file: %v", err)
}
defer textFile.Close()
textReporter := vegeta.NewTextReporter(&metrics)
if err := textReporter.Report(textFile); err != nil {
t.Errorf("failed to write vegeta text report: %v", err)
}
// Reporte en formato JSON
jsonFile, err := os.Create("vegeta-" + endpoint + "-report.json")
if err != nil {
t.Fatalf("failed to create JSON report file: %v", err)
}
defer jsonFile.Close()
jsonReporter := vegeta.NewJSONReporter(&metrics)
if err := jsonReporter.Report(jsonFile); err != nil {
t.Errorf("failed to write vegeta JSON report: %v", err)
}
if metrics.Success < 0.95 {
t.Errorf("success rate too low: %.2f%%", metrics.Success*100)
}
t.Logf("99th percentile latency: %s", metrics.Latencies.P99)
}Tenemos que crear una funcion (test) por endpoint a validar
Se debe ejecutar el siguiente comando
go test -v ./performance-test/vegetaAl finalizar generara dos reportes vegeta-product-report.json y vegeta-product-report.txt
Debemos instalar swaggo/swag con el siguiente comando
go install github.com/swaggo/swag/cmd/swag@latestDebemos agregar las siguientes dependencias
go get github.com/swaggo/gin-swagger
go get github.com/swaggo/filesDebemos documentar los endpoints con comentarios ejemplo
// GetProducts godoc
// @Summary Lista todos los productos
// @Description Obtiene todos los productos disponibles
// @Tags products
// @Produce json
// @Success 200 {array} models.Product
// @Router /products [get]
func (ec *ProductController) GetProducts(c *gin.Context) {
// ...
}Swag generará la documentacion de swagger en base a esos comentarios
Para generar el archivo swagger simplemente ejecutamos el siguiente comando
swag initEsto nos generará los archivos de swagger en la carpeta /docs con la siguiente estructura
/docs ├── docs.go ├── swagger.json └── swagger.yml
Para configurar Swagger UI simplemente agregamos el siguiente codigo al archivo main.go
import (
"github.com/swaggo/gin-swagger"
"github.com/swaggo/files"
_ "go-gin/docs"
)
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))Quedando así
package main
import (
"go-gin/config"
"go-gin/routes"
_ "go-gin/docs" // docs generated by Swag
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
func main() {
// Show banner with application information
config.ShowBanner()
// Create a server instance
r := gin.Default()
// Config CORS
r.Use(cors.New(cors.Config{
AllowOrigins: []string{config.AppConfig.CORSOrigin},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{config.AppConfig.CORSHeaders},
AllowCredentials: true,
}))
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// Config server path
routerGroup := r.Group(config.AppConfig.ServerPath)
// Config routes for the server
routes.SetupRoutes(routerGroup)
// Start server with port
r.Run(":" + config.AppConfig.ServerPort)
}Cuando ejecutemos a la aplicacion debemos entrar a la pagina /swagger/index.html
A continuación dejo links utilizados para realizar este proyecto