Heim > Artikel > Backend-Entwicklung > Pytest und PostgreSQL: Frische Datenbank für jeden Test
In Pytest, dem beliebtesten Python-Testframework aller, ist ein Fixture ein wiederverwendbarer Codeabschnitt, der etwas anordnet, bevor der Test beginnt, und nach dem Beenden aufräumt. Zum Beispiel eine temporäre Datei oder einen temporären Ordner, eine Setup-Umgebung, das Starten eines Webservers usw. In diesem Beitrag sehen wir uns an, wie man ein Pytest-Fixture erstellt, das eine Testdatenbank (leer oder mit bekanntem Status) erstellt, die abgerufen wird bereinigt, sodass jeder Test auf einer völlig sauberen Datenbank ausgeführt werden kann.
Wir werden mit Psycopg 3 ein Pytest-Gerät erstellen, um die Testdatenbank vorzubereiten und zu bereinigen. Da eine leere Datenbank zum Testen selten hilfreich ist, werden wir optional Yoyo-Migrationen anwenden (zum Zeitpunkt des Schreibens ist die Website nicht verfügbar, gehen Sie zum Snapshot von archive.org), um sie aufzufüllen.
Die Anforderungen für das Pytest-Fixture namens test_db, das in diesem Blogbeitrag erstellt wurde, sind also:
def test_create_admin_table(test_db): ...Erhält eine reguläre Psycopg Connection-Instanz, die mit der Test-DB verbunden ist. Der Test kann alles tun, was er benötigt, wie bei der allgemeinen Verwendung von Psycopg, z. B.:
def test_create_admin_table(test_db): # Open a cursor to perform database operations cur = test_db.cursor() # Pass data to fill a query placeholders and let Psycopg perform # the correct conversion (no SQL injections!) cur.execute( "INSERT INTO test (num, data) VALUES (%s, %s)", (100, "abc'def")) # Query the database and obtain data as Python objects. cur.execute("SELECT * FROM test") cur.fetchone() # will return (1, 100, "abc'def") # You can use `cur.fetchmany()`, `cur.fetchall()` to return a list # of several records, or even iterate on the cursor for record in cur: print(record)
Es sieht so aus, als gäbe es einige Pytest-Plugins, die PostgreSQL-Fixtures für Tests versprechen, die auf Datenbanken basieren. Sie könnten für Sie gut funktionieren.
Ich habe pytest-postgresql ausprobiert, was dasselbe verspricht. Ich habe es versucht, bevor ich mein eigenes Fixture geschrieben habe, aber ich konnte es nicht für mich zum Laufen bringen. Vielleicht, weil ihre Dokumente für mich sehr verwirrend waren.
├── src │ └── tuvok │ ├── __init__.py │ └── sales │ └── new_user.py ├── tests │ ├── conftest.py │ └── sales │ └── test_new_user.py ├── requirements.txt └── yoyo.iniWenn Sie eine Migrationsbibliothek wie das fantastische Yoyo verwenden, befinden sich Migrationsskripte wahrscheinlich in migrations/:
├── migrations ├── 20240816_01_Yn3Ca-sales-user-user-add-last-run-table.py ├── ...Konfiguration
# Without DB name! TEST_DB_URL = "postgresql://localhost" TEST_DB_NAME = "test_tuvok" TEST_DB_MIGRATIONS_DIR = str(Path(__file__, "../../migrations").resolve())Sie können diese Werte über die Umgebungsvariable oder was auch immer für Ihren Fall geeignet ist festlegen.
Erstellen Sie test_db-Fixture
PostgreSQL- und Psycopg-Bibliothek schreiben Sie das Fixture in conftest.py:
@pytest.fixture def test_db(): # autocommit=True start no transaction because CREATE/DROP DATABASE # cannot be executed in a transaction block. with psycopg.connect(TEST_DB_URL, autocommit=True) as conn: cur = conn.cursor() # create test DB, drop before cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB_NAME}" WITH (FORCE)') cur.execute(f'CREATE DATABASE "{TEST_DB_NAME}"') # Return (a new) connection to just created test DB # Unfortunately, you cannot directly change the database for an existing Psycopg connection. Once a connection is established to a specific database, it's tied to that database. with psycopg.connect(TEST_DB_URL, dbname=TEST_DB_NAME) as conn: yield conn cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB_NAME}" WITH (FORCE)')Erstellen Sie eine Migrationseinrichtung
Yoyo-Migrationen. Schreiben Sie apply migrations als ein weiteres Fixture mit dem Namen yoyo:
@pytest.fixture def yoyo(): # Yoyo expect `driver://user:pass@host:port/database_name?param=value`. # In passed URL we need to url = ( urlparse(TEST_DB_URL) . # 1) Change driver (schema part) with `postgresql+psycopg` to use # psycopg 3 (not 2 which is `postgresql+psycopg2`) _replace(scheme="postgresql+psycopg") . # 2) Change database to test db (in which migrations will apply) _replace(path=TEST_DB_NAME) .geturl() ) backend = get_backend(url) migrations = read_migrations(TEST_DB_MIGRATIONS_DIR) if len(migrations) == 0: raise ValueError(f"No Yoyo migrations found in '{TEST_DB_MIGRATIONS_DIR}'") with backend.lock(): backend.apply_migrations(backend.to_apply(migrations))Wenn Sie
Migrationen auf jede Testdatenbank anwenden möchten, benötigen Sie ein Yoyo-Fixture für test_db-Fixture:
@pytest.fixture def test_db(yoyo): ...Um
die Migration nur auf einige Tests anzuwenden, erfordern Sie Yoyo einzeln:
def test_create_admin_table(test_db, yoyo): ...Abschluss
Ich hoffe, dieser Artikel hat Ihnen bei Ihrer eigenen Datenbank-Testsuite geholfen. Hinterlassen Sie mir Ihre Frage gerne in den Kommentaren und viel Spaß beim Codieren!
Das obige ist der detaillierte Inhalt vonPytest und PostgreSQL: Frische Datenbank für jeden Test. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!