Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Ent es un framework de entidades de código abierto para Go. Es similar a los ORMs tradicionales, pero tiene características distintivas que lo han hecho muy popular en la comunidad de Go. Ent fue liberado como código abierto por Ariel en 2019, cuando trabajaba en Facebook. Ent surgió de las dificultades para gestionar el desarrollo de aplicaciones con modelos de datos muy grandes y complejos, y funcionó con éxito dentro de Facebook durante un año antes de su publicación. Tras graduarse del programa de código abierto de Facebook, Ent se unió a la Linux Foundation en septiembre de 2021.
Este tutorial está dirigido a principiantes en Ent y Go que quieran comenzar construyendo un proyecto simple: un sistema de gestión de contenido mínimo.
En los últimos años, Ent se ha convertido en uno de los ORMs de más rápido crecimiento en Go:

Source: @ossinsight_bot on Twitter, November 2022
Algunas de las características más destacadas de Ent son:
API de Go con tipos seguros para trabajar con tu base de datos. Olvídate de usar
interface{}o reflexión para interactuar con tu base de datos. Usa Go puro que tu editor entienda y tu compilador verifique.
Modela tus datos con semántica de grafos - Ent utiliza semántica de grafos para modelar los datos de tu aplicación. Esto facilita enormemente recorrer conjuntos de datos complejos con una API simple.
Supongamos que queremos obtener todos los usuarios que están en grupos relacionados con perros. Aquí hay dos formas de escribir esto con Ent:
// Start traversing from the topic.
client.Topic.Query().
Where(topic.Name("dogs")).
QueryGroups().
QueryUsers().
All(ctx)
// OR: Start traversing from the users and filter.
client.User.Query().
Where(
user.HasGroupsWith(
group.HasTopicsWith(
topic.Name("dogs"),
),
),
).
All(ctx)Generación automática de servidores - ya necesites GraphQL, gRPC o una capa API compatible con OpenAPI, Ent puede generar el código necesario para crear un servidor de alto rendimiento sobre tu base de datos. Ent generará tanto los esquemas de terceros (tipos GraphQL, mensajes Protobuf, etc.) como código optimizado para las tareas repetitivas de lectura y escritura en la base de datos.
Integrado con Atlas - Ent se construye con una rica integración con Atlas, una herramienta robusta de gestión de esquemas con muchas capacidades avanzadas. Atlas puede planificar automáticamente migraciones de esquemas, así como verificarlas en CI o implementarlas en producción por ti. (Revelación completa: Ariel y yo somos los creadores y mantenedores)
Requisitos previos
Puedes encontrar el código mostrado en este tutorial en este repositorio.
Paso 1: Configurar el esquema de la base de datos
Puedes encontrar el código descrito en este paso en este commit.
Comencemos inicializando nuestro proyecto usando go mod init:
go mod init github.com/rotemtam/ent-blog-example
Go confirma que se ha creado nuestro nuevo módulo:
go: creating new go.mod: module github.com/rotemtam/ent-blog-example
Lo primero que abordaremos en nuestro proyecto de demostración será configurar nuestra base de datos. Crearemos nuestro modelo de datos de la aplicación usando Ent. Vamos a instalarlo con go get:
go get -u entgo.io/ent@master
Una vez instalado, podemos usar la CLI de Ent para inicializar los modelos de los dos tipos de entidades con los que trabajaremos en este tutorial: User y Post.
go run -mod=mod entgo.io/ent/cmd/ent new User Post
Observa que se crean varios archivos:
.
`-- ent
|-- generate.go
`-- schema
|-- post.go
`-- user.go
2 directories, 3 files
Ent creó la estructura básica de nuestro proyecto:
generate.go: veremos en un momento cómo este archivo se usa para invocar el motor de generación de código de Ent.El directorio
schema, con unent.Schemabásico para cada entidad solicitada.
Continuemos definiendo el esquema para nuestras entidades. Esta es la definición del esquema para User:
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("email").
Unique(),
field.Time("created_at").
Default(time.Now),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("posts", Post.Type),
}
}
Observa que definimos tres campos: name, email y created_at (que toma el valor predeterminado de time.Now()). Como esperamos que los emails sean únicos en nuestro sistema, añadimos esa restricción en el campo email. Además, definimos una arista llamada posts hacia el tipo Post. Las aristas se usan en Ent para definir relaciones entre entidades. Al trabajar con bases de datos relacionales, las aristas se traducen en claves foráneas y tablas de asociación.
// Post holds the schema definition for the Post entity.
type Post struct {
ent.Schema
}
// Fields of the Post.
func (Post) Fields() []ent.Field {
return []ent.Field{
field.String("title"),
field.Text("body"),
field.Time("created_at").
Default(time.Now),
}
}
// Edges of the Post.
func (Post) Edges() []ent.Edge {
return []ent.Edge{
edge.From("author", User.Type).
Unique().
Ref("posts"),
}
}
En el esquema Post, definimos también tres campos: title, body y created_at. Además, definimos una arista llamada author desde Post hacia la entidad User. Marcamos esta arista como Unique porque en nuestro incipiente sistema, cada publicación solo puede tener un autor. Usamos Ref para indicar a Ent que la referencia inversa de esta arista es la arista posts en User.
El poder de Ent proviene de su motor de generación de código. Al desarrollar con Ent, cada vez que hacemos cambios en el esquema de la aplicación, debemos invocar el motor de generación de código para regenerar nuestro código de acceso a la base de datos. Esto es lo que permite a Ent mantener una API Go segura en tipos y eficiente para nosotros.
Veámoslo en acción, ejecuta:
go generate ./...
Observa que se crearon muchos archivos Go nuevos para nosotros:
.
`-- ent
|-- client.go
|-- context.go
|-- ent.go
|-- enttest
| `-- enttest.go
/// .. Truncated for brevity
|-- user_query.go
`-- user_update.go
9 directories, 29 files
Si te interesa ver cómo es el esquema real de la base de datos para nuestra aplicación, puedes usar una herramienta útil llamada entviz:
go run -mod=mod ariga.io/entviz ./ent/schema
Para ver el resultado, haz clic aquí.
Una vez definido nuestro modelo de datos, creemos el esquema de la base de datos.
Para instalar la última versión de Atlas, simplemente ejecuta uno de los siguientes comandos en tu terminal, o visita el sitio web de Atlas:
- macOS + Linux
- Homebrew
- Go
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
go install ariga.io/atlas/cmd/atlas@master
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Con Atlas instalado, podemos crear el script de migración inicial:
atlas migrate diff add_users_posts \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
Observa que se crearon dos archivos nuevos:
ent/migrate/migrations
|-- 20230226150934_add_users_posts.sql
`-- atlas.sum
El archivo SQL (el nombre real variará en tu máquina según la fecha y hora en que ejecutes atlas migrate diff) contiene las sentencias SQL DDL necesarias para configurar el esquema de la base de datos en una base de datos MySQL vacía:
-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `created_at` timestamp NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `email` (`email`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
-- create "posts" table
CREATE TABLE `posts` (`id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `body` longtext NOT NULL, `created_at` timestamp NOT NULL, `user_posts` bigint NULL, PRIMARY KEY (`id`), INDEX `posts_users_posts` (`user_posts`), CONSTRAINT `posts_users_posts` FOREIGN KEY (`user_posts`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE SET NULL) CHARSET utf8mb4 COLLATE utf8mb4_bin;
Para configurar nuestro entorno de desarrollo, usemos Docker para ejecutar un contenedor local de mysql:
docker run --rm --name entdb -d -p 3306:3306 -e MYSQL_DATABASE=ent -e MYSQL_ROOT_PASSWORD=pass mysql:8
Finalmente, ejecutemos el script de migración en nuestra base de datos local:
atlas migrate apply --dir file://ent/migrate/migrations \
--url mysql://root:pass@localhost:3306/ent
Atlas reporta que creó las tablas correctamente:
Migrating to version 20230220115943 (1 migrations in total):
-- migrating version 20230220115943
-> CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `created_at` timestamp NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `email` (`email`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
-> CREATE TABLE `posts` (`id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `body` longtext NOT NULL, `created_at` timestamp NOT NULL, `post_author` bigint NULL, PRIMARY KEY (`id`), INDEX `posts_users_author` (`post_author`), CONSTRAINT `posts_users_author` FOREIGN KEY (`post_author`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE SET NULL) CHARSET utf8mb4 COLLATE utf8mb4_bin;
-- ok (55.972329ms)
-------------------------
-- 67.18167ms
-- 1 migrations
-- 2 sql statements
Paso 2: Poblar nuestra base de datos
El código para este paso se puede encontrar en este commit.
Mientras desarrollamos nuestro sistema de gestión de contenidos, sería triste cargar una página web y no ver contenido. Comencemos insertando datos en nuestra base de datos y aprendiendo algunos conceptos de Ent.
Para acceder a nuestra base de datos MySQL local, necesitamos un controlador. Usa go get para obtenerlo:
go get -u github.com/go-sql-driver/mysql
Crea un archivo llamado main.go y añade este script básico de inicialización:
package main
import (
"context"
"flag"
"fmt"
"log"
"github.com/rotemtam/ent-blog-example/ent"
_ "github.com/go-sql-driver/mysql"
"github.com/rotemtam/ent-blog-example/ent/user"
)
func main() {
// Read the connection string to the database from a CLI flag.
var dsn string
flag.StringVar(&dsn, "dsn", "", "database DSN")
flag.Parse()
// Instantiate the Ent client.
client, err := ent.Open("mysql", dsn)
if err != nil {
log.Fatalf("failed connecting to mysql: %v", err)
}
defer client.Close()
ctx := context.Background()
// If we don't have any posts yet, seed the database.
if !client.Post.Query().ExistX(ctx) {
if err := seed(ctx, client); err != nil {
log.Fatalf("failed seeding the database: %v", err)
}
}
// ... Continue with server start.
}
func seed(ctx context.Context, client *ent.Client) error {
// Check if the user "rotemtam" already exists.
r, err := client.User.Query().
Where(
user.Name("rotemtam"),
).
Only(ctx)
switch {
// If not, create the user.
case ent.IsNotFound(err):
r, err = client.User.Create().
SetName("rotemtam").
SetEmail("r@hello.world").
Save(ctx)
if err != nil {
return fmt.Errorf("failed creating user: %v", err)
}
case err != nil:
return fmt.Errorf("failed querying user: %v", err)
}
// Finally, create a "Hello, world" blogpost.
return client.Post.Create().
SetTitle("Hello, World!").
SetBody("This is my first post").
SetAuthor(r).
Exec(ctx)
}
Como puedes ver, este programa primero verifica si existe alguna entidad Post en la base de datos. Si no existe, ejecuta la función seed. Esta función utiliza Ent para recuperar el usuario llamado rotemtam de la base de datos y, si no existe, intenta crearlo. Finalmente, la función crea una entrada de blog con este usuario como autor.
Ejecútalo:
go run main.go -dsn "root:pass@tcp(localhost:3306)/ent?parseTime=true"
Paso 3: Creando la página de inicio
El código descrito en este paso se encuentra en este commit
Vamos a crear ahora la página principal del blog. Constará de varias partes:
La vista - una plantilla Go html/template que renderiza el HTML real que verán los usuarios.
El código del servidor - contiene los manejadores de peticiones HTTP que comunicarán los navegadores de nuestros usuarios y renderizarán nuestras plantillas con datos obtenidos de la base de datos.
El enrutador - registra diferentes rutas a manejadores.
Una prueba unitaria - para verificar que nuestro servidor se comporta correctamente.
La vista
Go incluye un excelente motor de plantillas con dos variantes: text/template para renderizar texto genérico y html/template que añade características de seguridad adicionales para prevenir inyección de código al trabajar con documentos HTML. Más información aquí.
Creemos nuestra primera plantilla para mostrar una lista de entradas del blog. Crea un archivo llamado templates/list.tmpl:
<html>
<head>
<title>My Blog</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
</head>
<body>
<div class="col-lg-8 mx-auto p-4 py-md-5">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<a href="/" class="d-flex align-items-center text-dark text-decoration-none">
<span class="fs-4">Ent Blog Demo</span>
</a>
</header>
<main>
<div class="row g-5">
<div class="col-md-12">
{{- range . }}
<h2>{{ .Title }}</h2>
<p>
{{ .CreatedAt.Format "2006-01-02" }} by {{ .Edges.Author.Name }}
</p>
<p>
{{ .Body }}
</p>
{{- end }}
</div>
</div>
</main>
<footer class="pt-5 my-5 text-muted border-top">
<p>
This is the Ent Blog Demo. It is a simple blog application built with Ent and Go. Get started:
</p>
<pre>go get entgo.io/ent</pre>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"></script>
</body>
</html>
Aquí usamos una versión modificada de la Plantilla Inicial de Bootstrap como base de nuestra interfaz. Destacamos las partes importantes: en nuestro manejador index, pasaremos a esta plantilla un slice de objetos Post.
Dentro de la plantilla Go, los datos que pasamos están disponibles como ".". Esta línea usa range para iterar sobre cada entrada:
{{- range . }}
Luego, mostramos el título, fecha de creación y nombre del autor mediante la relación Author:
<h2>{{ .Title }}</h2>
<p>
{{ .CreatedAt.Format "2006-01-02" }} by {{ .Edges.Author.Name }}
</p>
Finalmente, mostramos el cuerpo de la entrada y cerramos el bucle.
<p>
{{ .Body }}
</p>
{{- end }}
Tras definir la plantilla, debemos hacerla disponible en nuestro programa. Incrustamos esta plantilla en nuestro binario usando el paquete embed (documentación):
var (
//go:embed templates/*
resources embed.FS
tmpl = template.Must(template.ParseFS(resources, "templates/*"))
)
Código del servidor
Continuamos definiendo un tipo llamado server y su constructor newServer. Esta estructura tendrá métodos receptores para cada manejador HTTP y vinculará el cliente Ent creado al inicio con el código del servidor.
type server struct {
client *ent.Client
}
func newServer(client *ent.Client) *server {
return &server{client: client}
}
Definamos ahora el manejador para nuestra página principal del blog. Esta página debe contener una lista de todas las entradas disponibles:
// index serves the blog home page
func (s *server) index(w http.ResponseWriter, r *http.Request) {
posts, err := s.client.Post.
Query().
WithAuthor().
All(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, posts); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Centrémonos en el código de Ent que recupera las entradas de la base de datos:
// s.client.Post contains methods for interacting with Post entities
s.client.Post.
// Begin a query.
Query().
// Retrieve the entities using the `Author` edge. (a `User` instance)
WithAuthor().
// Run the query against the database using the request context.
All(r.Context())
El enrutador
Para gestionar las rutas de nuestra aplicación, usemos go-chi, una popular biblioteca de enrutamiento para Go.
go get -u github.com/go-chi/chi/v5
Definimos la función newRouter que configura nuestro enrutador:
// newRouter creates a new router with the blog handlers mounted.
func newRouter(srv *server) chi.Router {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", srv.index)
return r
}
En esta función, primero instanciamos un nuevo chi.Router, luego registramos dos middlewares:
middleware.Loggeres un registrador de acceso básico que imprime información sobre cada petición que maneja nuestro servidor.middleware.Recovererse recupera de los pánicos en nuestros manejadores, evitando que todo el servidor se caiga debido a un error en la aplicación.
Finalmente, registramos la función index de la estructura server para manejar solicitudes GET en la ruta / de nuestro servidor.
Una prueba unitaria
Antes de conectar todo, escribamos una prueba unitaria simple para verificar que nuestro código funciona como se espera.
Para simplificar nuestras pruebas instalaremos el controlador SQLite para Go, que nos permite usar una base de datos en memoria:
go get -u github.com/mattn/go-sqlite3
A continuación, instalamos testify, una biblioteca de utilidades que se usa comúnmente para escribir aserciones en pruebas.
go get github.com/stretchr/testify
Con estas dependencias instaladas, crea un nuevo archivo llamado main_test.go:
package main
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
_ "github.com/mattn/go-sqlite3"
"github.com/rotemtam/ent-blog-example/ent/enttest"
"github.com/stretchr/testify/require"
)
func TestIndex(t *testing.T) {
// Initialize an Ent client that uses an in memory SQLite db.
client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer client.Close()
// seed the database with our "Hello, world" post and user.
err := seed(context.Background(), client)
require.NoError(t, err)
// Initialize a server and router.
srv := newServer(client)
r := newRouter(srv)
// Create a test server using the `httptest` package.
ts := httptest.NewServer(r)
defer ts.Close()
// Make a GET request to the server root path.
resp, err := ts.Client().Get(ts.URL)
// Assert we get a 200 OK status code.
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
// Read the response body and assert it contains "Hello, world!"
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Hello, World!")
}
Ejecuta la prueba para verificar que nuestro servidor funciona correctamente:
go test ./...
Observa que nuestra prueba pasa:
ok github.com/rotemtam/ent-blog-example 0.719s
? github.com/rotemtam/ent-blog-example/ent [no test files]
? github.com/rotemtam/ent-blog-example/ent/enttest [no test files]
? github.com/rotemtam/ent-blog-example/ent/hook [no test files]
? github.com/rotemtam/ent-blog-example/ent/migrate [no test files]
? github.com/rotemtam/ent-blog-example/ent/post [no test files]
? github.com/rotemtam/ent-blog-example/ent/predicate [no test files]
? github.com/rotemtam/ent-blog-example/ent/runtime [no test files]
? github.com/rotemtam/ent-blog-example/ent/schema [no test files]
? github.com/rotemtam/ent-blog-example/ent/user [no test files]
Integrando todo
Finalmente, actualicemos nuestra función main para integrar todo:
func main() {
// Read the connection string to the database from a CLI flag.
var dsn string
flag.StringVar(&dsn, "dsn", "", "database DSN")
flag.Parse()
// Instantiate the Ent client.
client, err := ent.Open("mysql", dsn)
if err != nil {
log.Fatalf("failed connecting to mysql: %v", err)
}
defer client.Close()
ctx := context.Background()
// If we don't have any posts yet, seed the database.
if !client.Post.Query().ExistX(ctx) {
if err := seed(ctx, client); err != nil {
log.Fatalf("failed seeding the database: %v", err)
}
}
srv := newServer(client)
r := newRouter(srv)
log.Fatal(http.ListenAndServe(":8080", r))
}
¡Ahora podemos ejecutar nuestra aplicación y maravillarnos con nuestro logro: una página principal de blog funcional!
go run main.go -dsn "root:pass@tcp(localhost:3306)/test?parseTime=true"

Paso 4: Añadiendo contenido
Puedes seguir los cambios de este paso en este commit.
Ningún sistema de gestión de contenido estaría completo sin la capacidad, bueno, de gestionar contenido. Demostremos cómo podemos añadir soporte para publicar nuevas entradas en nuestro blog.
Empecemos creando el manejador del backend:
// add creates a new blog post.
func (s *server) add(w http.ResponseWriter, r *http.Request) {
author, err := s.client.User.Query().Only(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := s.client.Post.Create().
SetTitle(r.FormValue("title")).
SetBody(r.FormValue("body")).
SetAuthor(author).
Exec(r.Context()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
http.Redirect(w, r, "/", http.StatusFound)
}
Como puedes ver, el manejador actualmente carga el único usuario de la tabla users (ya que aún no hemos creado un sistema de gestión de usuarios ni capacidades de inicio de sesión). Only fallará a menos que se recupere exactamente un resultado de la base de datos.
A continuación, nuestro manejador crea una nueva entrada, estableciendo los campos de título y cuerpo con los valores recuperados de r.FormValue. Aquí es donde Go almacena toda la entrada del formulario pasada a una solicitud HTTP.
Después de crear el manejador, debemos conectarlo a nuestro enrutador:
// newRouter creates a new router with the blog handlers mounted.
func newRouter(srv *server) chi.Router {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", srv.index)
r.Post("/add", srv.add)
return r
}
A continuación, podemos añadir un componente HTML <form> que será usado por nuestro usuario para escribir su contenido:
<div class="col-md-12">
<hr/>
<h2>Create a new post</h2>
<form action="/add" method="post">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input name="title" type="text" class="form-control" id="title" placeholder="Once upon a time..">
</div>
<div class="mb-3">
<label for="body" class="form-label">Body</label>
<textarea name="body" class="form-control" id="body" rows="8"></textarea>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary mb-3">Post</button>
</div>
</form>
</div>
Además, añadamos un toque agradable: mostrar las entradas del blog de la más nueva a la más antigua. Para hacer esto, modifica el manejador index para ordenar las entradas en orden descendente usando la columna created_at:
posts, err := s.client.Post.
Query().
WithAuthor().
Order(ent.Desc(post.FieldCreatedAt)).
All(ctx)
Finalmente, añadamos otra prueba unitaria que verifique que el flujo de añadir entradas funciona como se espera:
func TestAdd(t *testing.T) {
client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer client.Close()
err := seed(context.Background(), client)
require.NoError(t, err)
srv := newServer(client)
r := newRouter(srv)
ts := httptest.NewServer(r)
defer ts.Close()
// Post the form.
resp, err := ts.Client().PostForm(ts.URL+"/add", map[string][]string{
"title": {"Testing, one, two."},
"body": {"This is a test"},
})
require.NoError(t, err)
// We should be redirected to the index page and receive 200 OK.
require.Equal(t, http.StatusOK, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
// The home page should contain our new post.
require.Contains(t, string(body), "This is a test")
}
Ejecutemos la prueba:
go test ./...
¡Y todo funciona!
ok github.com/rotemtam/ent-blog-example 0.493s
? github.com/rotemtam/ent-blog-example/ent [no test files]
? github.com/rotemtam/ent-blog-example/ent/enttest [no test files]
? github.com/rotemtam/ent-blog-example/ent/hook [no test files]
? github.com/rotemtam/ent-blog-example/ent/migrate [no test files]
? github.com/rotemtam/ent-blog-example/ent/post [no test files]
? github.com/rotemtam/ent-blog-example/ent/predicate [no test files]
? github.com/rotemtam/ent-blog-example/ent/runtime [no test files]
? github.com/rotemtam/ent-blog-example/ent/schema [no test files]
? github.com/rotemtam/ent-blog-example/ent/user [no test files]
Una prueba unitaria que pasa es genial, pero verifiquemos nuestros cambios visualmente:

¡Nuestro formulario aparece - genial! Después de enviarlo:

Nuestra nueva entrada se muestra. ¡Bien hecho!
Para finalizar
En esta publicación demostramos cómo construir una aplicación web simple con Ent y Go. Nuestra aplicación es definitivamente básica, pero cubre muchas de las bases que necesitarás al construir una aplicación: definir tu modelo de datos, gestionar el esquema de tu base de datos, escribir código de servidor, definir rutas y construir una interfaz de usuario.
Como suele pasar con el contenido introductorio, solo hemos tocado la punta del iceberg de lo que puedes hacer con Ent, pero espero que hayas probado algunas de sus características principales.
- Suscríbete a nuestro Newsletter
- Síguenos en Twitter
- Únete a #ent en Gophers Slack
- Únete al Ent Discord Server