⚡ Erreur permission PostgreSQL : tests Laravel (1 fix)
Erreur permission PostgreSQL sous Laravel : découvrez comment corriger les droits du schéma, isoler la base de test et fiabiliser PHPUnit. Fix rapide !

Résoudre une erreur de permission PostgreSQL dans les tests Laravel
Un test Laravel échoue parfois avant même de toucher au code qu'il est censé vérifier. Dans le journal, PostgreSQL indique que l'utilisateur n'a pas le droit de créer une table dans le schéma public. Le truc qui déroute, c'est que l'application arrive à se connecter à la base... mais Laravel plante quand il essaie de créer sa table migrations.
Ce cas rappelle une nuance importante : avoir accès à une base PostgreSQL ne veut pas dire automatiquement pouvoir y créer des tables. Les droits s'appliquent à plusieurs niveaux, et les migrations ont besoin d'autorisations sur le schéma concerné. Et si les tests pointent sur une autre base que celle prévue (mauvaise config, cache, variables d'environnement...), vous pouvez aussi vous retrouver à modifier une base de développement par accident.
Voici une approche prudente : diagnostiquer l'origine exacte, isoler une base de test, corriger les permissions utiles, puis vérifier côté Laravel/PHPUnit. On regardera aussi ce que fait vraiment RefreshDatabase, comment interpréter un avertissement PHP à côté, et quoi faire après un premier test de migration.
Comprendre l'erreur de permission PostgreSQL
Dans un test Laravel, les migrations peuvent être lancées avant même que les assertions ne s'exécutent. Laravel doit alors créer (ou consulter) la table migrations, qui garde la trace des migrations déjà appliquées. Si le rôle PostgreSQL utilisé par les tests n'a pas les droits requis sur le schéma, l'exécution s'arrête avec une erreur du style :
SQLSTATE[42501]: Insufficient privilege: 7
ERROR: permission denied for schema public
SQL: create table "migrations" (...)
Le code 42501 indique un refus d'autorisation. Ce n'est ni un souci de syntaxe SQL côté test, ni un problème « mystérieux » dans le code : PostgreSQL bloque simplement la création de l'objet demandé.
Les droits PostgreSQL ne se limitent pas à la base
Il faut distinguer plusieurs étages :
- La base de données : elle gère notamment la connexion et la création de schémas.
- Le schéma : il contient les tables, séquences, vues... et c'est là que Laravel crée en général ses tables (souvent dans
publicsi rien d'autre n'est configuré). - Les objets : chaque table (ou vue, séquence, etc.) a ses propres droits (lecture/écriture/suppression, etc.).
Donc oui, un rôle peut réussir à se connecter à samu_test et, malgré tout, ne pas avoir le droit de créer une table dans public. Donner CONNECT sur une base n'implique pas automatiquement CREATE sur chaque schéma qu'elle contient.
Dans beaucoup de setups, la base de test appartient à un compte admin (souvent postgres), alors que Laravel se connecte avec un rôle applicatif. Dans ce cas, il faut vérifier qui est propriétaire de quoi, et surtout quels droits ont réellement été accordés au rôle utilisé par Laravel.
Commencer par vérifier la configuration réelle
Avant de « bidouiller » les permissions, assurez-vous que l'échec vient bien du bon niveau PostgreSQL. Notez le nom de la base, le nom du rôle et le schéma visé par Laravel.
Une config approximative peut produire un diagnostic trompeur : les tests peuvent utiliser une valeur issue de phpunit.xml, de .env.testing ou d'une configuration mise en cache.
Vérifiez aussi que les tests ne pointent pas vers la base de développement. Une commande de migration, ou un trait de test qui remet la base à zéro, peut supprimer/recréer des tables. La logique simple : d'abord sécuriser la cible, ensuite corriger les droits. Et évitez de « tout corriger » en donnant tous les droits au compte utilisé partout : c'est pratique sur le moment, mais dangereux à long terme.
Pour les autorisations à l'échelle bases/schémas/objets, voir la documentation PostgreSQL sur les privilèges. Les droits « par défaut » liés à public peuvent changer selon la version et la configuration : ne partez pas du principe qu'un rôle a toujours le même comportement.
Créer une base de test et régler les droits
Les tests automatisés peuvent lancer des migrations, utiliser des transactions et remettre la base dans un état connu. Ils ne doivent donc pas utiliser la base de développement (encore moins une base de production). Créez une base dédiée, par exemple samu_test, et configurez Laravel pour ne s'y connecter que pendant les tests.
Cette séparation aide autant côté sécurité que côté reproductibilité : vous évitez qu'une migration ou un nettoyage de données touche un environnement utilisé ailleurs.
Examiner la base et son propriétaire
Sur une machine où vous pouvez utiliser postgres avec sudo, listez les bases :
sudo -u postgres psql -l
Exemple :
Name | Owner
-----------+----------
postgres | postgres
samu | samu_user
samu_test | postgres
Ici, samu_test existe, mais son propriétaire est postgres. Ce n'est pas forcément une erreur : ça veut surtout dire que le rôle applicatif (samu_user) ne possède pas automatiquement tous les droits nécessaires sur cette base.
Vérifiez aussi le propriétaire et les privilèges du schéma public :
sudo -u postgres psql -d samu_test -c "\dn+ public"
Cette commande permet de voir s'il y a un décalage entre le propriétaire de la base, celui du schéma, et le rôle utilisé par Laravel.
Propriété et/ou permissions explicites
Pour une base dédiée uniquement aux tests, une option simple consiste à donner au rôle applicatif la propriété de la base et du schéma :
sudo -u postgres psql -c \
"ALTER DATABASE samu_test OWNER TO samu_user;"
sudo -u postgres psql -d samu_test -c \
"ALTER SCHEMA public OWNER TO samu_user;"
Ces commandes exigent un rôle autorisé à modifier les propriétaires. Elles vont bien si vous avez décidé que samu_user doit administrer cette base de test. En revanche, ne les appliquez pas « comme ça » sur une base partagée ou un environnement plus critique.
Autre approche : garder un propriétaire distinct et accorder les permissions nécessaires au rôle applicatif. Par exemple :
GRANT CONNECT ON DATABASE samu_test TO samu_user;
GRANT USAGE, CREATE ON SCHEMA public TO samu_user;
CONNECTpermet de se connecter à la base.USAGEdonne le droit d'utiliser le schéma.CREATEautorise la création d'objets dans ce schéma.
À adapter selon votre politique d'accès. Et surtout : exécutez les droits au bon niveau (ce n'est pas le même endroit pour les droits de base et pour ceux du schéma).
L'idée n'est pas d'ouvrir tous les privilèges possibles : donnez au rôle de test exactement ce dont les migrations ont besoin sur la base dédiée. Et évitez d'utiliser postgres comme compte permanent de l'application : ça masque le problème au lieu de le corriger et rend plus probable un impact en cas de mauvaise cible.
Configurer PHPUnit et préparer la base avec Laravel
Ensuite, vérifiez que PHPUnit et Laravel utilisent bien les mêmes paramètres. Le nom de la base doit être identique partout : config Laravel, phpunit.xml, et variables d'environnement utilisées pendant l'exécution. Sinon, corriger les permissions ne sert à rien si vos tests ne pointent pas sur la bonne base.
Définir la connexion de test
Dans phpunit.xml, vous pouvez définir les variables d'environnement pour l'exécution « testing » :
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="pgsql"/>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_PORT" value="5432"/>
<env name="DB_DATABASE" value="samu_test"/>
<env name="DB_USERNAME" value="samu_user"/>
Rappel rapide :
| Paramètre | Rôle |
|---|---|
APP_ENV |
Indique à Laravel qu'il est en environnement de test. |
DB_CONNECTION |
Pilote PostgreSQL (pgsql). |
DB_HOST |
Adresse du serveur. |
DB_PORT |
Port PostgreSQL (souvent 5432). |
DB_DATABASE |
Base dédiée aux tests. |
DB_USERNAME |
Rôle utilisé par Laravel. |
Si l'authentification nécessite un mot de passe, ajoutez aussi le secret via le mécanisme approprié à votre environnement (variables protégées, gestionnaire de secrets, config locale exclue du dépôt, etc.).
Laravel peut aussi charger .env.testing. Vérifiez qu'il ne contient pas des valeurs contradictoires avec phpunit.xml, notamment DB_DATABASE et DB_USERNAME. Après une modif, pensez à vider le cache de configuration :
php artisan config:clear
La documentation Laravel sur les tests et celle sur la configuration des bases de données décrivent le comportement selon la version. Le point pratique : ne supposez pas que « votre intention » est ce qui est chargé. Regardez ce qui est effectivement utilisé.
Utiliser RefreshDatabase avec un objectif clair
Le trait RefreshDatabase aide à remettre la base dans un état cohérent entre tests. Selon votre configuration, il peut s'appuyer sur les migrations et des transactions pour isoler les opérations. Mais il ne « répare » pas les permissions PostgreSQL : si le rôle n'a pas le droit de créer les tables nécessaires, RefreshDatabase échouera aussi.
Exemple de test qui vérifie qu'une migration de table est bien enregistrée :
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class MenuCategoryApiTest extends TestCase
{
use RefreshDatabase;
public function test_menu_categories_table_exists(): void
{
$this->assertDatabaseHas('migrations', [
'migration' => '2026_09_18_034452_create_menu_categories_table',
]);
}
}
Ce test vérifie qu'une ligne existe dans l'historique des migrations, et que le nom correspond exactement à celui du fichier de migration. Il ne vérifie pas, à lui seul, que toutes les colonnes et contraintes sont correctes.
RefreshDatabase est super quand chaque test doit démarrer d'un état maîtrisé. Mais l'utiliser sans comprendre son effet peut ralentir la suite, ou provoquer des surprises si la connexion cible n'est pas celle que vous pensiez. Avant d'ajouter le trait, confirmez au moins l'environnement, le nom de la base, et le comportement attendu de vos migrations.
Vérifier le résultat et poursuivre les tests
Une fois les droits et la config PHPUnit corrigés, relancez un test ciblé :
php artisan config:clear
php artisan test --filter=MenuCategoryApiTest
Si ça passe, vous avez déjà la preuve que :
- Laravel a utilisé la connexion attendue,
- il a pu accéder à la base,
- et l'assertion du test s'est exécutée.
Ça ne garantit pas que tous les parcours de l'API fonctionnent, ni que chaque contrainte de données est correcte. C'est une étape de diagnostic, pas une validation complète.
Distinguer le problème PostgreSQL des avertissements PHP
Il arrive aussi que le test « passe » tout en affichant un avertissement de dépréciation PHP provenant d'une dépendance, par exemple nunomaduro/collision. Un message sur un paramètre « implicitement nullable » ne parle pas de permissions PostgreSQL : ce sont deux problèmes séparés.
Pour trier le vrai du bruit, relevez les versions :
php -v
composer show nunomaduro/collision
composer show laravel/framework
composer show phpunit/phpunit
Avant de mettre à jour un paquet, vérifiez la compatibilité avec votre version de Laravel, PHPUnit et PHP. Une mise à jour « au hasard » peut faire disparaître un avertissement... tout en introduisant un souci ailleurs. En pratique : traitez d'abord les erreurs bloquantes, puis revenez sur les avertissements.
Tester le comportement métier, pas seulement l'existence d'une table
Vérifier que la table migrations contient la migration attendue est utile pour valider la chaîne de préparation de la base. Mais pour une API de catégories de menu, vous voulez des tests qui collent au comportement réel exposé.
Par exemple :
| Cas à tester | Résultat attendu |
|---|---|
| Lister les catégories | La réponse contient les données attendues et le bon code HTTP. |
| Créer une catégorie | Une donnée valide est enregistrée et renvoyée correctement. |
| Valider les entrées | Une donnée invalide est rejetée avec les erreurs attendues. |
| Afficher une catégorie | La ressource demandée est renvoyée, ou une réponse adaptée si elle n'existe pas. |
| Modifier une catégorie | Les changements sont persistés et visibles dans la réponse. |
| Supprimer une catégorie | L'enregistrement est supprimé conformément aux règles de l'API. |
| Gérer les doublons | Les règles d'unicité du produit sont respectées. |
| Vérifier les relations | Les références vers un entrepôt ou une section de cuisine sont correctes. |
Vous pouvez aussi tester les migrations (colonnes, clés étrangères, contraintes). Puis lancez la suite complète avec php artisan test, idéalement dans un environnement CI configuré pareil. Si vous exécutez en parallèle, assurez-vous aussi que le mécanisme de séparation des bases/données entre processus est en place : chaque exécution doit avoir son espace.
Enfin, en production, n'importez pas « tel quel » les droits utilisés en test. Un rôle capable de créer des tables est pratique pour les migrations, mais une application en fonctionnement a souvent besoin de droits plus limités. Les besoins ne sont pas les mêmes entre migration, exécution de l'application et tests automatisés : ces étapes n'ont pas à partager le même compte.