- pgmock est un serveur PostgreSQL simulé en mémoire pour les tests unitaires et les tests E2E, qui s’exécute en WebAssembly dans Node.js et dans le navigateur sans dépendances externes
- Les utilisateurs de
node-postgres peuvent se connecter via un objet de configuration obtenu sans ouvrir de port, et cette méthode fonctionne aussi dans le navigateur
- Dans le navigateur, une web app ne peut pas ouvrir de port TCP, mais peut utiliser
PostgresMock.createSocket et la configuration node-postgres ; si le bundler analyse les imports statiquement, des avertissements peuvent apparaître au sujet de modules Node.js optionnels
- L’implémentation exécute actuellement le serveur PostgreSQL dans un émulateur x86, en privilégiant l’absence de différences de comportement entre les tests et la production plutôt que les performances
- À long terme, lorsqu’un fork WASM natif de PostgreSQL sera suffisamment mature, le projet prévoit de proposer les deux approches, puis de basculer le mode natif WASM comme option par défaut
Ce que propose pgmock
- pgmock est un serveur PostgreSQL simulé en mémoire pour les tests unitaires et les tests E2E
- Il ne nécessite aucune dépendance externe et s’exécute dans WebAssembly à la fois sur Node.js et dans le navigateur
- L’installation se fait via npm
npm install pgmock
Flux d’utilisation de base
- Le serveur en mémoire se crée avec
PostgresMock.create(), et listen(5432) permet d’obtenir une chaîne de connexion
import { PostgresMock } from "pgmock";
const mock = await PostgresMock.create();
const connectionString = await mock.listen(5432);
- Si vous utilisez
node-postgres, mock.getNodePostgresConfig() fournit un objet de configuration permettant de se connecter sans écouter sur un port
- Une fois le travail terminé, il est recommandé d’appeler
mock.destroy() pour libérer les ressources
mock.destroy();
Prise en charge du navigateur et différence avec pglite
pgmock prend entièrement en charge les environnements navigateur
- Une web app ne peut pas ouvrir de port TCP, mais elle peut utiliser
PostgresMock.createSocket et la configuration node-postgres
- Si le bundler analyse les imports de manière statique, il peut émettre des avertissements indiquant l’absence de modules Node.js optionnels ; un exemple de configuration Webpack se trouve dans
examples/web-demo/next.config.mjs
- Si vous souhaitez uniquement exécuter une base de données dans le navigateur, vous pouvez envisager pglite
- pglite est plus rapide et plus léger, mais son ensemble de fonctionnalités est limité
pgmock a été conçu avec pour objectif une parité fonctionnelle avec PostgreSQL en production dans un environnement de test
Comment PostgreSQL est exécuté dans WebAssembly
- Il existe deux approches pour exécuter PostgreSQL dans WebAssembly
- L’approche par fork natif WASM est plus rapide et consomme beaucoup moins de mémoire, mais elle ne prend en charge que le mode mono-utilisateur, sans connexions ni extensions
pgmock utilise actuellement l’approche par émulateur x86
- Le but est d’éviter les écarts entre les tests et la production
- En test, les performances ne sont généralement pas le principal problème
- À moyen terme, lorsque le fork WASM natif de PostgreSQL sera plus mature, le projet prévoit de proposer les deux options
- Ensuite, il prévoit de faire du WASM natif l’option par défaut, et n’anticipe pas beaucoup de breaking changes en dehors de l’API interne
PostgresMock.subtle
Différences avec les projets PostgreSQL existants dans le navigateur
pgmock offre une compatibilité fonctionnelle complète à l’intérieur du runtime JavaScript et ne dépend pas d’un proxy réseau pour la communication
- Il simule la pile réseau en JavaScript pour reproduire le comportement d’un vrai réseau, et peut ainsi simuler des connexions TCP même sur les plateformes qui n’autorisent pas l’accès direct aux sockets bruts
Extensibilité et projets liés
- D’autres images Docker ou bases de données pourraient théoriquement être exécutées, mais cela n’a pas été testé
- Les implémentations et projets de base suivants sont mentionnés
- v86 : émulateur x86
- Supabase & Snaplet : base de l’approche consistant à exécuter PostgreSQL dans WebAssembly
- Stackframe : société mentionnée comme ayant financé le développement de
pgmock
1 commentaires
Avis sur Hacker News
Depuis quelques mois, nous développions en interne une version en mémoire de Postgres, avec une parité fonctionnelle avec la base de données de production.
L’avantage est qu’elle ne nécessite ni processus externe ni proxy. Si la plateforme peut exécuter WASM, on peut faire tourner pgmock dans Node.js ou dans un navigateur, et créer une nouvelle base de données avec des données mockées est aussi simple que de créer un objet JavaScript.
C’est un peu différent de pglite, qui nous a poussés à rendre pgmock open source. pgmock exécute le Postgres original dans un émulateur x86, tandis que pglite compile directement un fork de Postgres en WASM natif, ce qui le rend plus rapide et plus léger.
En revanche, pglite ne prend en charge que le mode mono-utilisateur et certaines extensions, donc il n’est pas possible de s’y connecter avec un client Postgres standard, ce qui est assez important pour les tests E2E.
En théorie, on pourrait l’adapter pour exécuter n’importe quelle image Docker sur une plateforme WebAssembly ; je serais curieux de savoir s’il y a des cibles précises que vous aimeriez voir.
Nous réfléchissons à plusieurs façons d’ajouter un mode multi-connexion, mais cela risque de prendre un peu de temps. PGlite a aussi d’autres limitations liées au mode mono-utilisateur : par exemple, pg_notify n’est pas encore pris en charge, et nous prévoyons également de corriger cela.
En revanche, ce projet est beaucoup plus proche d’un vrai Postgres, donc il a de bonnes chances de fonctionner tel quel. Ces projets de Postgres en mémoire semblent capables de réduire les temps d’exécution des tests à moins d’un quart, et ils ont un gros potentiel dans le domaine du test.
C’est mon point de vue en tant que personne travaillant sur PGlite.
Récemment, je voulais exécuter un pipeline FFMPEG/SoX côté client, mais il y avait tellement de dépendances qu’il était difficile de tout recompiler facilement avec Emscripten. Je me demande si cette approche pourrait aussi aider dans ce genre de cas.
Grâce aux fonctionnalités relationnelles, on pourrait ajouter et interroger en même temps les riches métadonnées métier spécifiques que l’on trouve généralement dans les bases de données relationnelles.
Quand on exécute
select foo();, on obtientError.captureStackTrace is not a function, sous Firefox 124.0.2 sur Linux.Je me demande s’il ne suffirait pas de lancer Postgres en mettant ses fichiers sur un ramdisk.
Mise à jour : comme cela peut tourner dans un navigateur/Node, les tests peuvent apparemment créer, modifier et supprimer des bases. Je suis trop développeur backend pour bien voir l’avantage par rapport à un environnement de développement classique. J’aimerais bien qu’on m’explique où, quand et en quoi c’est mieux.
La raison du choix de WebAssembly est que cela permet de rendre le comportement plus portable entre plateformes, architectures, navigateurs et environnements edge, tout en obtenant une configuration sans dépendance externe, qui ne nécessite même pas Docker.
Comme l’émulateur permet de démarrer directement depuis un état déjà en cours d’exécution, le démarrage de la base émulée est plus rapide que le lancement d’une vraie base de données ou d’un conteneur Docker. Cela dit, c’est plutôt un effet obtenu par chance qu’un objectif de conception.
Je me demande pourquoi ne pas utiliser quelque chose comme https://testcontainers.com/. Est-ce vraiment si grave qu’un moteur de conteneurs soit une dépendance externe ?
Dès qu’on y introduit des mocks, cela devient un test unitaire. C’est utile, mais ce n’est pas la même chose. L’un des points clés de l’E2E est justement que l’absence de mocks permet de savoir que le test est fidèle. Ici, on ne teste pas Postgres : on teste ce système à chaque fois.
Si l’on développe un PG embarqué, léger et peu performant, cela peut avoir du sens comme test de validation avant de lents vrais tests E2E. J’ai moi-même un cas d’usage de ce type.
À part ça, c’est un beau projet, et cela ressemble à un outil utile quand on a besoin d’un shim PG.
Pour Postgres, nous utilisons des savepoints, mais même sur ramdisk ce n’est pas si rapide.
Avant, dans les tests, on faisait tourner toutes sortes de faux serveurs en mémoire personnalisés. Aujourd’hui, on exécute la vraie cible avec https://testcontainers.com.
Si un développeur Prisma/Node.js veut simplement un « Postgres en boîte » pour le développement local, la version récemment transformée en serveur de PGlite, pglite-server, peut être un meilleur choix : https://github.com/kamilogorek/pglite-server
C’est plus rapide et cela peut conserver les données sur le système de fichiers, mais en cas d’utilisation intensive c’est moins stable qu’un serveur de tests E2E basé sur un émulateur x86 complet. pglite-server n’utilisait que 150 Mo de mémoire, contre 830 Mo pour pgmock-server.
Il suffit de checkout un nouveau
.env.localavec dotenv et de changer leDATABASE_URLde tous les scripts d’exécutionpackage.jsonnextjs/prisma.DATABASE_URL="postgresql://postgres@localhost:5432/awesomeproject""db:pushlocal": "dotenv -e .env.local -- pnpm prisma db push"C’est très facile à intégrer à n’importe quel projet, et je comprends que Neon sponsorise ce domaine.
Je ne veux pas jouer les rabat-joie, mais je ne pense pas utiliser ça.
Pour une application simple, ça peut fonctionner, mais dès que la complexité augmente — risque de deadlock, dépendance à la forme de la base de données, etc. — de petites différences de comportement peuvent devenir des problèmes critiques, ce qui en réduit l’intérêt.
Ces derniers temps, je préfère les environnements E2E avec contraintes de ressources. Cela donne au runner de tests local une chance de casser quand quelqu’un écrit du code vraiment inefficace.
Par ailleurs, créer un snapshot de la base de données au bout de quelques secondes puis déployer ce snapshot sur les partitions de test est très rapide, et m’a souvent fait gagner plusieurs minutes sur des suites de tests.
C’est une idée intéressante et une bonne expérience d’apprentissage, mais je pense que le public visé est limité.
Le titre est un peu ambigu. Si ça a été « construit au travail », j’aurais tendance à penser que, puisqu’il a utilisé les ressources de l’entreprise, la propriété intellectuelle de ce projet appartient à l’employeur.
Je me demande donc si, techniquement, il a le droit de le publier en open source.
Copyright 2024 Stackframe.. L’auteur semble travailler chez Stackframe.Je me demande comment cela se compare au mode de compatibilité Postgres de H2.
Plutôt chouette. Si vous pouvez répondre, j’ai quelques questions.
Je me demande ce qui a poussé l’entreprise à créer ce projet, et si lancer Postgres dans un conteneur Docker était trop lent.
J’aimerais aussi savoir en quoi la configuration CI pour les tests E2E a changé avant et après l’intégration de pgmock dans le workflow.
Je me demande également si la migration vers cette solution a été difficile.
Si vous dumpez les données de production, supprimez toutes les données sensibles, puis tronquez les tables inutiles comme les tables de logs, vous obtenez une bonne copie pour le développement.
Il suffit ensuite de la répliquer vers le développement, la QA, l’E2E, etc. Ce dont l’E2E a besoin, ce sont précisément ces extensions, triggers, fonctions, vues, index et données.
Je me demande s’il ne suffirait pas d’utiliser Docker et une base de données de test séparée.
Elixir fait comme ça, et le framework de test enveloppe chaque test dans une transaction puis effectue un rollback pour assurer l’isolation. Ce serait intéressant de savoir quel est l’avantage de cette approche.