Lección 8 de 8, y la que convierte lo aprendido en un proyecto de verdad: cómo se reparte el código en paquetes, cómo se prueba y qué más trae la caja de herramientas. Todo esto viene con Go: no hay que elegir un sistema de construcción ni una biblioteca de pruebas.
Paquetes
Un paquete es una carpeta. Los archivos de dentro declaran el mismo package y se ven entre sí sin importar nada.
// tienda/precio.go
package tienda
// ConIVA devuelve el precio con el IVA aplicado.
func ConIVA(base, tipo float64) float64 {
return redondear(base * (1 + tipo/100))
}
// redondear es privada: en minúscula, solo se ve dentro del paquete.
func redondear(v float64) float64 {
return float64(int(v*100+0.5)) / 100
}
Para usarlo desde fuera se importa por su ruta completa, que empieza por el nombre del módulo:
// main.go
package main
import (
"fmt"
"ejemplo/caja/tienda"
)
func main() {
fmt.Printf("%.2f\n", tienda.ConIVA(100, 21))
}
salida121.00
public ni private: lo que empieza por mayúscula se exporta, lo que empieza por minúscula no. ConIVA se puede llamar desde main; redondear, solo desde dentro de tienda. La misma regla vale para tipos, campos y constantes.Pruebas
Un archivo que acabe en _test.go, funciones que empiecen por Test y reciban *testing.T. No hace falta instalar nada.
La forma habitual en Go es la prueba de tabla: los casos en un slice y un bucle que los recorre.
// tienda/precio_test.go
package tienda
import "testing"
func TestConIVA(t *testing.T) {
casos := []struct {
nombre string
base float64
tipo float64
quiero float64
}{
{"iva del 21", 100, 21, 121},
{"iva del 10", 50, 10, 55},
{"sin iva", 30, 0, 30},
{"redondeo", 19.99, 21, 24.19},
}
for _, c := range casos {
t.Run(c.nombre, func(t *testing.T) {
if got := ConIVA(c.base, c.tipo); got != c.quiero {
t.Errorf("ConIVA(%v, %v) = %v, quería %v", c.base, c.tipo, got, c.quiero)
}
})
}
}
go test ./...
salida? ejemplo/caja [no test files] ok ejemplo/caja/tienda 0.003s
Añadir un caso es añadir una línea. Y el t.Run con el nombre hace que, cuando algo falle, el mensaje diga exactamente cuál:
si falla--- FAIL: TestConIVA (0.00s)
--- FAIL: TestConIVA/redondeo (0.00s)
precio_test.go:20: ConIVA(19.99, 21) = 24.19, quería 24.2
FAIL
Dice el caso, el archivo, la línea, lo que salió y lo que esperabas. Por eso el mensaje de t.Errorf conviene escribirlo con ese formato: qué llamaste, qué obtuviste, qué querías.
go test -race ./... es el hábito que de verdad paga, sobre todo si hay goroutines por medio. Lo vimos en la lección anterior.go vet: lo que compila pero está mal
nombre := "Ana"
fmt.Printf("hola %d\n", nombre) // %d para una cadena
go vet ./...
salida./vet.go:7:19: fmt.Printf format %d has arg nombre of wrong type string
Eso compila perfectamente y en ejecución imprime una cosa rara. go vet lo caza antes. Viene con Go y conviene pasarlo junto con las pruebas.
Dependencias
No hay que declarar nada a mano. Se importa y se pide a Go que ordene:
go mod tidy
salidago: finding module for package github.com/google/uuid go: downloading github.com/google/uuid v1.6.0 go: found github.com/google/uuid in github.com/google/uuid v1.6.0
go.modmodule ejemplo/caja go 1.25.14 require github.com/google/uuid v1.6.0
go mod tidy funciona en los dos sentidos: añade lo que falta y quita lo que ya no se usa. Al borrar el import y volver a ejecutarlo, la línea require desaparece sola. Aparece además un go.sum con las huellas de cada dependencia, y los dos archivos se guardan en el repositorio.
Compilar para otro sistema
Esto es de las cosas que más sorprenden al llegar de otros lenguajes. Dos variables de entorno y ya:
GOOS=windows GOARCH=amd64 go build -o caja.exe . GOOS=darwin GOARCH=arm64 go build -o caja-mac .
salida2.3M caja.exe 2.3M caja-mac
Hecho desde Linux, sin Windows ni un Mac a la vista y sin instalar nada. Es la misma propiedad de la que hablábamos en la guía: el binario se lleva todo dentro.
La caja de herramientas completa
| Orden | Para qué |
|---|---|
go run . |
Compilar y ejecutar mientras desarrollas |
go build -o nombre . |
Dejar el ejecutable |
go test ./... |
Todas las pruebas del proyecto |
go test -race ./... |
Lo mismo, cazando carreras |
go test -v -run TestConIVA |
Una sola prueba, con detalle |
go vet ./... |
Errores que compilan |
gofmt -w . |
Formatear |
go mod tidy |
Poner las dependencias al día |
go doc fmt.Println |
Documentación sin salir de la terminal |
Y hasta aquí
Con estas ocho lecciones tienes lo necesario para leer casi cualquier código de Go y escribir el tuyo: la forma del lenguaje, sus colecciones, sus interfaces implícitas, el trato que da a los errores, qué se copia y qué no, la concurrencia y las herramientas.
Lo que falta no es más sintaxis, es práctica. En la sección de ejemplos de Go hay casos resueltos —leer y escribir XML y JSON, montar un servidor web, conectar con una base de datos— que ya puedes seguir enteros. Y si quieres acompañarlo de las herramientas que rodean a cualquier proyecto, están las guías de Linux, Git y Docker.
Todo el código de esta lección se ejecutó con Go 1.25 antes de publicarla: el proyecto de ejemplo, las pruebas pasando y fallando, el aviso de go vet, la descarga real de la dependencia y los dos binarios cruzados, compilados desde Linux.