Unit-tests für die Abfrage in SQLAlchemy
Wie geht man zum testen von Abfragen in SQLAlchemy? Zum Beispiel nehmen wir an, wir haben diese models.py
from sqlalchemy import (
Column,
Integer,
String,
)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Panel(Base):
__tablename__ = 'Panels'
id = Column(Integer, primary_key=True)
category = Column(Integer, nullable=False)
platform = Column(String, nullable=False)
region = Column(String, nullable=False)
def __init__(self, category, platform, region):
self.category = category
self.platform = platform
self.region = region
def __repr__(self):
return (
"<Panel('{self.category}', '{self.platform}', "
"'{self.region}')>".format(self=self)
)
und diese tests.py
import unittest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base, Panel
class TestQuery(unittest.TestCase):
engine = create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=engine)
session = Session()
def setUp(self):
Base.metadata.create_all(self.engine)
self.session.add(Panel(1, 'ion torrent', 'start'))
self.session.commit()
def tearDown(self):
Base.metadata.drop_all(self.engine)
def test_query_panel(self):
expected = [Panel(1, 'ion torrent', 'start')]
result = self.session.query(Panel).all()
self.assertEqual(result, expected)
Wenn wir versuchen läuft der test schlägt fehl, obwohl die beiden Platten identisch Aussehen.
$ nosetests
F
======================================================================
FAIL: test_query_panel (tests.TestQuery)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/clasher/tmp/tests.py", line 31, in test_query_panel
self.assertEqual(result, expected)
AssertionError: Lists differ: [<Panel('1', 'ion torrent', 's... != [<Panel('1', 'ion torrent', 's...
First differing element 0:
<Panel('1', 'ion torrent', 'start')>
<Panel('1', 'ion torrent', 'start')>
[<Panel('1', 'ion torrent', 'start')>, <Panel('2', 'ion torrent', 'end')>]
----------------------------------------------------------------------
Ran 1 test in 0.063s
FAILED (failures=1)
Einer Lösung, die ich gefunden habe, ist eine Abfrage für jede einzelne Instanz ich erwarten, zu finden in der Abfrage:
class TestQuery(unittest.TestCase):
...
def test_query_panel(self):
expected = [
(1, 'ion torrent', 'start'),
(2, 'ion torrent', 'end')
]
successful = True
# Check to make sure every expected item is in the query
try:
for category, platform, region in expected:
self.session.query(Panel).filter_by(
category=category, platform=platform,
region=region).one()
except (NoResultFound, MultipleResultsFound):
successful = False
self.assertTrue(successful)
# Check to make sure no unexpected items are in the query
self.assertEqual(self.session.query(Panel).count(),
len(expected))
Dies erscheint mir als ziemlich hässlich, obwohl, und ich bin auch nicht immer zu dem Punkt, wo ich haben eine komplexe gefilterten Abfrage, die ich versuche zu testen. Gibt es eine elegantere Lösung, oder muss ich immer manuell eine Reihe von individuellen Abfragen?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ihre original-test ist auf dem richtigen Weg, Sie müssen nur eines von zwei Dingen tun: entweder stellen Sie sicher, dass zwei
Panel
Objekte mit dem gleichen Primärschlüssel Identität vergleichen, wieTrue
:oder Sie organisieren Ihre Prüfung, so dass Sie sicher, dass Sie die Prüfung gegen die gleichen
Panel
Instanz (denn hier nutzen wir die Identität anzeigen):so weit wie der Motor/das session setup/teardown, ich würde für zu gehen ein Muster, wo Sie mit einem einzigen Motor für alle tests, und vorausgesetzt, Ihr schema fixiert ist, ein einziges schema für alle tests, dann machen Sie sicher, dass die Daten mit denen Sie arbeiten erfolgt innerhalb einer Transaktion, die rückgängig gemacht werden können. Die
Session
gemacht werden kann, auf diese Weise zu arbeiten, so dass der Aufrufcommit()
eigentlich nicht Begehen die "echte" Transaktion, durch die Verpackung die gesamte Prüfung in einer explizitenTransaction
. Das Beispiel bei https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#joining-a-session-into-an-external-transaction-such-as-for-test-suites zeigt diese Verwendung. Mit einem ":memory:" Motor auf jeden Prüfadapter dauern wird, bis eine Menge Speicher, und nicht wirklich skalieren zu anderen Datenbanken neben SQLite.self
, und diese später abrufen, nicht durch das Abfragen der Datenbank wieder, aber durch solcheself
Attribute. Auch die Umsetzung__eq__
unnötig war; es scheint, dass SQLAlchemy zurück, die genau die gleiche Instanz von einem Modell (d.h.,created_model_instance is instance_from_query
zurückTrue
). Schließlich, es würde helfen, zu überarbeiten, die Antwort zu verwenden, die Transaktion-rollback-Muster, obwohl es sein kann, erschließt sich aus der Lektüre der SQLAlchemy-Dokumentation unter dem angegebenen link.