I've had good experience with testcontainers (https://testcontainers.com/) to do that sort of thing.
My current setup:
- generate a new psql testcontainer _or_ reuse an existing one by using a fixed name for the container - connect to the psql container with no database selected - create a new database using a random database name - connect to the randomly generated database - initialize the project's tables - run a test - drop the database - keep the testcontainer up and running and reuse with next test
With this setup, most tests run sub-second;
And tests can often use a DB transaction to rollback, unless the code under test already uses them.