- pgmock es un servidor simulado de PostgreSQL en memoria para pruebas unitarias y E2E, que se ejecuta con WebAssembly en Node.js y en el navegador sin dependencias externas
- Quienes usan
node-postgres pueden conectarse mediante un objeto de configuración que no abre puertos, y este enfoque también funciona en el navegador
- En el navegador, una app web no puede abrir puertos TCP, pero puede usar
PostgresMock.createSocket y la configuración de node-postgres; si el bundler analiza imports estáticos, pueden aparecer advertencias sobre módulos opcionales de Node.js
- La implementación actualmente ejecuta un servidor PostgreSQL dentro de un emulador x86, priorizando evitar diferencias de comportamiento entre pruebas y producción por encima del rendimiento
- A largo plazo, cuando madure el fork nativo de PostgreSQL para WASM, planean ofrecer ambos enfoques y luego cambiar el WASM nativo para que sea el valor predeterminado
Lo que ofrece pgmock
- pgmock es un servidor simulado de PostgreSQL en memoria para pruebas unitarias y E2E
- No requiere dependencias externas y se ejecuta dentro de WebAssembly tanto en Node.js como en el navegador
- Se puede instalar con npm
npm install pgmock
Flujo básico de uso
- El servidor en memoria se crea con
PostgresMock.create() y se puede obtener una cadena de conexión con listen(5432)
import { PostgresMock } from "pgmock";
const mock = await PostgresMock.create();
const connectionString = await mock.listen(5432);
- Si usas
node-postgres, mock.getNodePostgresConfig() proporciona un objeto de configuración que permite conectarse sin escuchar en un puerto
- Al terminar, se recomienda llamar a
mock.destroy() para liberar recursos
mock.destroy();
Soporte para navegador y diferencia con pglite
pgmock ofrece soporte completo para el entorno de navegador
- Las apps web no pueden abrir puertos TCP, pero pueden usar
PostgresMock.createSocket y la configuración de node-postgres
- Si el bundler analiza los imports de forma estática, pueden aparecer advertencias sobre módulos opcionales de Node.js ausentes; hay un ejemplo de configuración de Webpack en
examples/web-demo/next.config.mjs
- Si solo quieres ejecutar una base de datos en el navegador, puedes considerar pglite
- pglite es más rápido y liviano, pero tiene un conjunto de funciones limitado
pgmock está diseñado con el objetivo de lograr paridad funcional con PostgreSQL de producción en entornos de prueba
Cómo ejecuta PostgreSQL en WebAssembly
- Hay dos formas de ejecutar PostgreSQL en WebAssembly
- El enfoque de fork nativo para WASM es más rápido y usa mucha menos memoria, pero solo admite modo de usuario único y no soporta conexiones ni extensiones
pgmock actualmente usa el enfoque del emulador x86
- Su objetivo es evitar inconsistencias entre pruebas y producción
- Porque en las pruebas el rendimiento normalmente no es un gran problema
- A mediano plazo, cuando el fork nativo de PostgreSQL para WASM madure, planean ofrecer ambas opciones
- Más adelante planean cambiar el WASM nativo para que sea el valor predeterminado, y se espera que no haya muchos breaking changes importantes salvo en la API interna
PostgresMock.subtle
Diferencias con proyectos existentes de PostgreSQL en navegador
pgmock ofrece compatibilidad funcional completa dentro del runtime de JavaScript y no depende de un proxy de red para la comunicación
- Simula el stack de red en JavaScript para que se comporte como una red real, y puede simular conexiones TCP incluso en plataformas que no permiten acceso a raw sockets
Extensibilidad y proyectos relacionados
- En teoría también podrían ejecutarse otras imágenes de Docker u otras bases de datos, pero no se han probado
- Se mencionan las siguientes implementaciones y proyectos base relacionados
- v86: emulador x86
- Supabase & Snaplet: base del enfoque para ejecutar PostgreSQL dentro de WebAssembly
- Stackframe: mencionada como la empresa que pagó salarios durante el desarrollo de
pgmock
1 comentarios
Opiniones de Hacker News
Durante varios meses estuvimos construyendo en la empresa una versión de Postgres en memoria, con paridad funcional respecto a la base de datos de producción.
La ventaja es que no necesita procesos externos ni proxies. Si la plataforma puede ejecutar WASM, se puede correr pgmock incluso en Node.js o en el navegador, y crear una base de datos nueva con datos mock es tan simple como crear un objeto JavaScript.
Es un poco distinto de pglite, que nos llevó a publicar pgmock como open source. pgmock ejecuta el Postgres original dentro de un emulador x86, mientras que pglite compila directamente un fork de Postgres a WASM nativo, por lo que es más rápido y liviano.
Sin embargo, pglite solo soporta modo de un solo usuario y algunas extensiones, así que no se puede conectar con clientes Postgres normales, algo bastante importante para pruebas E2E.
En teoría, se podría adaptar para ejecutar cualquier imagen Docker en una plataforma WebAssembly; me da curiosidad si hay algún objetivo concreto que les gustaría ver.
Estamos pensando en varias formas de agregar un modo de múltiples conexiones, pero probablemente tome algo de tiempo. PGlite también tiene otras limitaciones relacionadas con el modo de un solo usuario; por ejemplo, todavía no soporta pg_notify, y también planeamos corregirlo.
En cambio, este proyecto se acerca mucho más al Postgres real, así que es muy probable que simplemente funcione. Estos proyectos de Postgres en memoria parecen capaces de reducir el tiempo de ejecución de las pruebas a menos de una cuarta parte, y tienen un gran potencial en el área de testing.
Es mi perspectiva como alguien que trabaja en PGlite.
Hace poco quería correr un pipeline de FFMPEG/SoX en el cliente, pero tenía tantas dependencias que era difícil recompilarlo fácilmente con Emscripten. Me pregunto si este enfoque también podría ayudar en casos así.
Gracias a las capacidades relacionales, se podría agregar y consultar junto con ella la rica metadata específica de dominio que normalmente vive en una base de datos relacional.
Al ejecutar
select foo();apareceError.captureStackTrace is not a function, y ocurre en Firefox 124.0.2 sobre Linux.Me pregunto si no bastaría con poner los archivos de Postgres en un ramdisk y ejecutarlo ahí.
Actualización: parece que puede ejecutarse en entornos de navegador/Node, así que las pruebas pueden crear, actualizar y borrar cosas. Soy demasiado desarrollador backend, así que no entiendo bien la ventaja frente a un entorno de desarrollo común. Me gustaría que alguien pudiera explicar dónde, cuándo y cómo es mejor.
La razón para hacerlo con WebAssembly es que permite ajustar el comportamiento de forma más portable entre plataformas, arquitecturas e incluso entornos de navegador o edge, además de quedar una configuración sin dependencias externas que ni siquiera necesita Docker.
Como el emulador permite arrancar directamente desde un estado ya ejecutado, iniciar la base de datos emulada es más rápido que levantar una base de datos real o un contenedor Docker. Aunque esto fue más bien un efecto afortunado que un objetivo de diseño.
Me pregunto si no se podría usar algo como https://testcontainers.com/. No sé si que el motor de contenedores sea una dependencia externa sea algo tan malo.
En cuanto metes mocks ahí dentro, se convierte en una prueba unitaria. Es útil, pero no es lo mismo. Uno de los puntos clave de E2E es que, al no haber mocks, sabes que la prueba es precisa. Esto no es probar Postgres, sino probar este sistema cada vez.
Si estás construyendo un PG para sistemas embebidos, livianos y de bajo rendimiento, tiene sentido como prueba de verificación antes de pruebas E2E reales más lentas. Yo también tengo un caso de uso así.
Fuera de eso, es un proyecto interesante y parece una herramienta útil cuando se necesita un shim de PG.
Para Postgres estamos usando savepoints, pero ni siquiera en ramdisk es tan rápido.
Antes corríamos todo tipo de servidores falsos en memoria personalizados en las pruebas. Hoy ejecutamos el objetivo real con https://testcontainers.com.
Si un desarrollador de Prisma/Node.js solo quiere “Postgres enlatado” para desarrollo local, quizá le convenga más pglite-server, una versión reciente de PGlite convertida en servidor: https://github.com/kamilogorek/pglite-server
Es más rápido y puede persistir datos en el sistema de archivos, pero bajo mucha carga es menos estable que un servidor E2E basado en un emulador x86 completo. pglite-server usa solo 150 MB de memoria, mientras que pgmock-server usó 830 MB.
Basta con hacer checkout de un nuevo
.env.localcon dotenv y cambiar elDATABASE_URLde todos los scripts de ejecución depackage.jsonpara nextjs/prisma.DATABASE_URL="postgresql://postgres@localhost:5432/awesomeproject""db:pushlocal": "dotenv -e .env.local -- pnpm prisma db push"Es muy fácil integrarlo en cualquier proyecto, y se entiende que Neon esté patrocinando este espacio.
No quiero ser aguafiestas, pero no pienso usar esto.
Puede que funcione para aplicaciones simples, pero si aumenta la complejidad —por ejemplo, si hay riesgo de deadlocks o se depende de la forma de la base de datos—, incluso pequeñas diferencias de comportamiento pueden convertirse en problemas críticos, así que su valor disminuye.
Hoy en día prefiero entornos E2E con recursos limitados, porque le dan al ejecutor de pruebas local la oportunidad de fallar cuando alguien escribe código extremadamente ineficiente.
Además, crear un snapshot de la base de datos después de unos segundos y desplegarlo en particiones de prueba es muy rápido, y muchas veces nos ha ahorrado varios minutos en la suite de pruebas.
Es una idea interesante y una buena experiencia de aprendizaje, pero creo que el público objetivo es limitado.
El título es un poco confuso. Si “lo hicieron en la empresa”, asumiendo que usaron recursos de la compañía, ¿no pertenecería la propiedad intelectual de este proyecto al empleador?
Si es así, me pregunto si técnicamente pueden publicarlo como open source.
Copyright 2024 Stackframe.. Parece que el autor trabaja en Stackframe.Me pregunto cómo se compara esto con el modo de compatibilidad con Postgres de H2.
Está bastante bueno. Si pueden responder, tengo algunas dudas.
Me pregunto qué motivó a la empresa a crear este proyecto y si ejecutar Postgres en un contenedor Docker era demasiado lento.
También quisiera saber cómo cambió la configuración de CI para pruebas E2E antes y después de integrar pgmock en el flujo.
Y me da curiosidad si fue difícil migrar a esta solución.
Si dumpeas los datos de producción, eliminas todos los datos sensibles y luego haces truncate de las tablas que no hacen falta, como las de logs, obtienes una buena copia para desarrollo.
Luego la replicás a desarrollo, QA, E2E, etc. Lo que E2E necesita es justamente esas extensiones, triggers, funciones, vistas, índices y datos.
Me pregunto si no bastaría con usar Docker y tener una base de datos de prueba separada.
Elixir lo hace así, y el framework de pruebas envuelve cada prueba en una transacción y hace rollback para el aislamiento. Sería interesante saber cuál es la ventaja de este enfoque.