Kapitel 4. Testen
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Die meisten Tests innerhalb von Anwendungen bestehen sowohl aus Unit- als auch aus Funktionstests. Mit SQLAlchemy kann es jedoch viel Arbeit sein, eine Abfrageanweisung oder ein Modell für Unit-Tests korrekt zu mocken. Diese Arbeit führt oft nicht zu einem wirklichen Gewinn gegenüber dem Testen gegen eine Datenbank während des Funktionstests. Das führt dazu, dass die Leute Wrapper-Funktionen für ihre Abfragen erstellen, die sie während der Unit-Tests einfach abbilden können, oder dass sie sowohl in den Unit- als auch in den Funktionstests nur gegen eine Datenbank testen. Ich persönlich verwende gerne kleine Wrapper-Funktionen, wenn es möglich ist, oder - wenn das aus irgendeinem Grund keinen Sinn macht oder ich mich in veraltetem Code befinde - Mockout-Funktionen.
In diesem Kapitel erfährst du, wie du funktionale Tests mit einer Datenbank durchführst und wie du SQLAlchemy-Abfragen und -Verbindungen mockst.
Testen mit einer Testdatenbank
Für unsere Beispielanwendung benötigen wir eine app.py-Datei, die unsere Anwendungslogik enthält, und eine db.py-Datei, die unsere Datenbanktabellen und -verbindungen enthält. Diese Dateien findest du im Ordner CH05/ des Beispielcodes.
Wie eine Anwendung strukturiert ist, ist ein Implementierungsdetail, das einen großen Einfluss darauf haben kann, wie du deine Tests durchführen musst. In db.py kannst du sehen, dass unsere Datenbank über die Klasse DataAccessLayer
eingerichtet wird. Wir verwenden diese Datenzugriffsklasse, um ein Datenbankschema zu initialisieren und es mit einer Engine zu verbinden, wann immer wir wollen. Dieses Muster wird häufig in Web-Frameworks in Verbindung mit SQLAlchemy verwendet. Die Klasse DataAccessLayer
wird ohne eine Engine und eine Verbindung in der Variable dal
initialisiert. Beispiel 4-1 zeigt einen Ausschnitt aus unserer db.py-Datei.
Beispiel 4-1. DataAccessLayer-Klasse
from
datetime
import
datetime
from
sqlalchemy
import
(
MetaData
,
Table
,
Column
,
Integer
,
Numeric
,
String
,
DateTime
,
ForeignKey
,
Boolean
,
create_engine
)
class
DataAccessLayer
:
connection
=
None
engine
=
None
conn_string
=
None
metadata
=
MetaData
(
)
cookies
=
Table
(
'
cookies
'
,
metadata
,
Column
(
'
cookie_id
'
,
Integer
(
)
,
primary_key
=
True
)
,
Column
(
'
cookie_name
'
,
String
(
50
)
,
index
=
True
)
,
Column
(
'
cookie_recipe_url
'
,
String
(
255
)
)
,
Column
(
'
cookie_sku
'
,
String
(
55
)
)
,
Column
(
'
quantity
'
,
Integer
(
)
)
,
Column
(
'
unit_cost
'
,
Numeric
(
12
,
2
)
)
)
def
db_init
(
self
,
conn_string
)
:
self
.
engine
=
create_engine
(
conn_string
or
self
.
conn_string
)
self
.
metadata
.
create_all
(
self
.
engine
)
self
.
connection
=
self
.
engine
.
connect
(
)
dal
=
DataAccessLayer
(
)
In der vollständigen Datei erstellen wir alle Tabellen, die wir seit Kapitel 1 verwendet haben, nicht nur die Cookies.
Damit kannst du eine Verbindung mit einem bestimmten Verbindungsstring wie eine Fabrik initialisieren.
So entsteht eine Instanz der Klasse
DataAccessLayer
, die in unserer gesamten Anwendung importiert werden kann.
Wir werden Tests für die Funktion get_orders_by_customer
schreiben, die wir in Kapitel 2 gebaut haben und die in der Datei app.py zu finden ist (siehe Beispiel 4-2).
Beispiel 4-2. app.py zum Testen
from
db
import
dal
from
sqlalchemy.sql
import
select
def
get_orders_by_customer
(
cust_name
,
shipped
=
None
,
details
=
False
)
:
columns
=
[
dal
.
orders
.
c
.
order_id
,
dal
.
users
.
c
.
username
,
dal
.
users
.
c
.
phone
]
joins
=
dal
.
users
.
join
(
dal
.
orders
)
if
details
:
columns
.
extend
(
[
dal
.
cookies
.
c
.
cookie_name
,
dal
.
line_items
.
c
.
quantity
,
dal
.
line_items
.
c
.
extended_cost
]
)
joins
=
joins
.
join
(
dal
.
line_items
)
.
join
(
dal
.
cookies
)
cust_orders
=
select
(
columns
)
cust_orders
=
cust_orders
.
select_from
(
joins
)
.
where
(
dal
.
users
.
c
.
username
==
cust_name
)
if
shipped
is
not
None
:
cust_orders
=
cust_orders
.
where
(
dal
.
orders
.
c
.
shipped
==
shipped
)
return
dal
.
connection
.
execute
(
cust_orders
)
.
fetchall
(
)
Dies ist unsere
DataAccessLayer
Instanz aus der Datei db.py .Da sich unsere Tabellen innerhalb des
dal
Objekts befinden, greifen wir von dort aus auf sie zu.
Schauen wir uns alle Möglichkeiten an, wie die Funktion get_orders_by_customer
verwendet werden kann. Für diese Übung gehen wir davon aus, dass wir bereits überprüft haben, dass die Eingaben für die Funktion vom richtigen Typ sind. Bei deinen Tests solltest du jedoch darauf achten, dass du mit Daten testest, die korrekt funktionieren, und mit Daten, die Fehler verursachen könnten. Hier ist eine Liste der Variablen, die unsere Funktion akzeptieren kann, und ihrer möglichen Werte:
-
cust_name
kann leer sein, eine Zeichenkette, die den Namen eines gültigen Kunden enthält, oder eine Zeichenkette, die nicht den Namen eines gültigen Kunden enthält. -
shipped
kannNone
,True
, oderFalse
sein. -
details
kannTrue
oderFalse
sein.
Wenn wir alle möglichen Kombinationen testen wollen, brauchen wir 12 (also 3 * 3 * 2) Tests, um diese Funktion vollständig zu testen.
Hinweis
Es ist wichtig, nicht Dinge zu testen, die nur Teil der Grundfunktionalität von SQLAlchemy sind, da SQLAlchemy bereits über eine große Sammlung gut geschriebener Tests verfügt. Wir wollen zum Beispiel keine einfachen Insert-, Select-, Delete- oder Update-Anweisungen testen, da diese bereits im SQLAlchemy-Projekt selbst getestet werden. Stattdessen solltest du Dinge testen, die dein Code verändert und die sich auf die Ausführung der SQLAlchemy-Anweisung oder die Ergebnisse auswirken könnten, die sie liefert.
Für dieses Testbeispiel auf verwenden wir das integrierte Modul unittest
. Mach dir keine Sorgen, wenn du mit diesem Modul nicht vertraut bist; wir werden dir die wichtigsten Punkte erklären. Zuerst müssen wir die Testklasse einrichten und die Verbindung von dal
initialisieren, wie in Beispiel 4-3 gezeigt, indem wir eine neue Datei namens test_app.py erstellen.
Beispiel 4-3. Einrichten der Tests
import
unittest
class
TestApp
(
unittest
.
TestCase
)
:
@classmethod
def
setUpClass
(
cls
)
:
dal
.
db_init
(
'
sqlite:///:memory:
'
)
unittest
erfordert Testklassen, die vonunittest.TestCase
geerbt werden.Die Methode
setUpClass
wird einmal für die gesamte Testklasse ausgeführt.Diese Zeile initialisiert eine Verbindung zu einer In-Memory-Datenbank für Tests.
Jetzt müssen wir einige Daten laden, die wir für unsere Tests verwenden wollen. Ich werde hier nicht den vollständigen Code einfügen, da es sich um dieselben Einfügungen handelt, mit denen wir in Kapitel 2 gearbeitet haben, nur modifiziert, um DataAccessLayer
zu verwenden; er ist in der Beispieldatei db.py verfügbar. Nachdem wir unsere Daten geladen haben, können wir nun einige Tests schreiben. Wir fügen diese Tests als Funktionen in die Klasse TestApp
ein, wie in Beispiel 4-4 gezeigt.
Beispiel 4-4. Die ersten sechs Tests für leere Benutzernamen
def
test_orders_by_customer_blank
(
self
)
:
results
=
get_orders_by_customer
(
'
'
)
self
.
assertEqual
(
results
,
[
]
)
def
test_orders_by_customer_blank_shipped
(
self
)
:
results
=
get_orders_by_customer
(
'
'
,
True
)
self
.
assertEqual
(
results
,
[
]
)
def
test_orders_by_customer_blank_notshipped
(
self
)
:
results
=
get_orders_by_customer
(
'
'
,
False
)
self
.
assertEqual
(
results
,
[
]
)
def
test_orders_by_customer_blank_details
(
self
)
:
results
=
get_orders_by_customer
(
'
'
,
details
=
True
)
self
.
assertEqual
(
results
,
[
]
)
def
test_orders_by_customer_blank_shipped_details
(
self
)
:
results
=
get_orders_by_customer
(
'
'
,
True
,
True
)
self
.
assertEqual
(
results
,
[
]
)
def
test_orders_by_customer_blank_notshipped_details
(
self
)
:
results
=
get_orders_by_customer
(
'
'
,
False
,
True
)
self
.
assertEqual
(
results
,
[
]
)
unittest
erwartet, dass jeder Test mit den Buchstabentest
beginnt.unittest
verwendetassertEqual
, um zu überprüfen, ob das Ergebnis mit dem übereinstimmt, was du erwartest. Da ein Benutzer nicht gefunden wird, solltest du eine leere Liste zurückbekommen.
Speichere die Testdatei als test_app.py und führe die Unit-Tests mit dem folgenden Befehl aus:
# python -m unittest test_app ...... Ran 6 tests in 0.018s
Hinweis
Es kann sein, dass du eine Warnung über SQLite und Dezimaltypen erhältst; ignoriere sie einfach, da sie für unsere Beispiele normal ist. Sie erscheint, weil SQLite keinen echten Dezimaltyp hat und SQLAlchemy dich darauf hinweisen will, dass es bei der Konvertierung vom SQLite-Float-Typ zu einigen Merkwürdigkeiten kommen könnte. Es ist immer ratsam, diese Meldungen zu untersuchen, denn im Produktionscode weisen sie dich normalerweise auf den richtigen Weg, den du einschlagen solltest. Wir lösen diese Warnung hier absichtlich aus, damit du siehst, wie sie aussieht.
Jetzt müssen wir ein paar Daten laden und sicherstellen, dass unsere Tests noch funktionieren. Wir wiederholen die Arbeit aus Kapitel 2 und fügen die gleichen Benutzer, Aufträge und Positionen ein. Dieses Mal werden wir die Daten jedoch in eine Funktion namens db_prep
einfügen. So können wir diese Daten vor einem Test mit einem einfachen Funktionsaufruf einfügen. Der Einfachheit halber habe ich diese Funktion in die Datei db.py eingefügt (siehe Beispiel 4-5); in der Praxis wird sie jedoch oft in einer Testfixture oder einer Dienstprogrammdatei zu finden sein.
Beispiel 4-5. Einfügen einiger Testdaten
def
prep_db
():
ins
=
dal
.
cookies
.
insert
()
dal
.
connection
.
execute
(
ins
,
cookie_name
=
'dark chocolate chip'
,
cookie_recipe_url
=
'http://some.aweso.me/cookie/recipe_dark.html'
,
cookie_sku
=
'CC02'
,
quantity
=
'1'
,
unit_cost
=
'0.75'
)
inventory_list
=
[
{
'cookie_name'
:
'peanut butter'
,
'cookie_recipe_url'
:
'http://some.aweso.me/cookie/peanut.html'
,
'cookie_sku'
:
'PB01'
,
'quantity'
:
'24'
,
'unit_cost'
:
'0.25'
},
{
'cookie_name'
:
'oatmeal raisin'
,
'cookie_recipe_url'
:
'http://some.okay.me/cookie/raisin.html'
,
'cookie_sku'
:
'EWW01'
,
'quantity'
:
'100'
,
'unit_cost'
:
'1.00'
}
]
dal
.
connection
.
execute
(
ins
,
inventory_list
)
customer_list
=
[
{
'username'
:
"cookiemon"
,
'email_address'
:
"mon@cookie.com"
,
'phone'
:
"111-111-1111"
,
'password'
:
"password"
},
{
'username'
:
"cakeeater"
,
'email_address'
:
"cakeeater@cake.com"
,
'phone'
:
"222-222-2222"
,
'password'
:
"password"
},
{
'username'
:
"pieguy"
,
'email_address'
:
"guy@pie.com"
,
'phone'
:
"333-333-3333"
,
'password'
:
"password"
}
]
ins
=
dal
.
users
.
insert
()
dal
.
connection
.
execute
(
ins
,
customer_list
)
ins
=
insert
(
dal
.
orders
)
.
values
(
user_id
=
1
,
order_id
=
'wlk001'
)
dal
.
connection
.
execute
(
ins
)
ins
=
insert
(
dal
.
line_items
)
order_items
=
[
{
'order_id'
:
'wlk001'
,
'cookie_id'
:
1
,
'quantity'
:
2
,
'extended_cost'
:
1.00
},
{
'order_id'
:
'wlk001'
,
'cookie_id'
:
3
,
'quantity'
:
12
,
'extended_cost'
:
3.00
}
]
dal
.
connection
.
execute
(
ins
,
order_items
)
ins
=
insert
(
dal
.
orders
)
.
values
(
user_id
=
2
,
order_id
=
'ol001'
)
dal
.
connection
.
execute
(
ins
)
ins
=
insert
(
dal
.
line_items
)
order_items
=
[
{
'order_id'
:
'ol001'
,
'cookie_id'
:
1
,
'quantity'
:
24
,
'extended_cost'
:
12.00
},
{
'order_id'
:
'ol001'
,
'cookie_id'
:
4
,
'quantity'
:
6
,
'extended_cost'
:
6.00
}
]
dal
.
connection
.
execute
(
ins
,
order_items
)
Da wir nun eine prep_db
Funktion haben, können wir diese in unserer test_app.py setUpClass
Methode verwenden, um Daten in die Datenbank zu laden, bevor wir unsere Tests durchführen. Unsere setUpClass
Methode sieht jetzt wie folgt aus:
@classmethod
def
setUpClass
(
cls
):
dal
.
db_init
(
'sqlite:///:memory:'
)
prep_db
()
Wir können diese Testdaten verwenden, um sicherzustellen, dass unsere Funktion das Richtige tut, wenn wir einen gültigen Benutzernamen erhalten. Diese Tests werden als neue Funktionen in unsere TestApp-Klasse eingefügt, wie Beispiel 4-6 zeigt.
Beispiel 4-6. Tests für einen gültigen Benutzer
def
test_orders_by_customer
(
self
):
expected_results
=
[(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
)]
results
=
get_orders_by_customer
(
'cookiemon'
)
self
.
assertEqual
(
results
,
expected_results
)
def
test_orders_by_customer_shipped_only
(
self
):
results
=
get_orders_by_customer
(
'cookiemon'
,
True
)
self
.
assertEqual
(
results
,
[])
def
test_orders_by_customer_unshipped_only
(
self
):
expected_results
=
[(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
)]
results
=
get_orders_by_customer
(
'cookiemon'
,
False
)
self
.
assertEqual
(
results
,
expected_results
)
def
test_orders_by_customer_with_details
(
self
):
expected_results
=
[
(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
,
u'dark chocolate chip'
,
2
,
Decimal
(
'1.00'
)),
(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
,
u'oatmeal raisin'
,
12
,
Decimal
(
'3.00'
))
]
results
=
get_orders_by_customer
(
'cookiemon'
,
details
=
True
)
self
.
assertEqual
(
results
,
expected_results
)
def
test_orders_by_customer_shipped_only_with_details
(
self
):
results
=
get_orders_by_customer
(
'cookiemon'
,
True
,
True
)
self
.
assertEqual
(
results
,
[])
def
test_orders_by_customer_unshipped_only_details
(
self
):
expected_results
=
[
(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
,
u'dark chocolate chip'
,
2
,
Decimal
(
'1.00'
)),
(
u'wlk001'
,
u'cookiemon'
,
u'111-111-1111'
,
u'oatmeal raisin'
,
12
,
Decimal
(
'3.00'
))
]
results
=
get_orders_by_customer
(
'cookiemon'
,
False
,
True
)
self
.
assertEqual
(
results
,
expected_results
)
Kannst du anhand der Tests in Beispiel 4-6 herausfinden, was mit einem anderen Benutzer passiert, z. B. cakeeater
? Was ist mit den Tests für einen Benutzernamen, der noch nicht im System existiert? Oder wie sieht das Ergebnis aus, wenn wir als Benutzernamen eine ganze Zahl statt eines Strings erhalten? Vergleiche deine Tests mit denen im mitgelieferten Beispielcode, wenn du fertig bist, um zu sehen, ob deine Tests den in diesem Buch verwendeten ähnlich sind.
Wir haben gelernt, wie wir SQLAlchemy in funktionalen Tests einsetzen können, um festzustellen, ob sich eine Funktion bei einem bestimmten Datensatz wie erwartet verhält. Außerdem haben wir uns angesehen, wie wir eine unittest
Datei einrichtet und wie wir die Datenbank für unsere Tests vorbereiten. Als Nächstes werden wir uns mit Tests beschäftigen, ohne die Datenbank anzugreifen.
Mocks verwenden
Diese Technik kann ein mächtiges Werkzeug sein, wenn du eine Testumgebung hast, in der das Erstellen einer Testdatenbank keinen Sinn macht oder einfach nicht machbar ist. Wenn du eine große Menge an Logik hast, die mit dem Ergebnis der Abfrage arbeitet, kann es nützlich sein, den SQLAlchemy-Code so zu mocken, dass er die gewünschten Werte zurückgibt, damit du nur die umgebende Logik testen kannst. Wenn ich einen Teil der Abfrage ausspiegele, erstelle ich normalerweise die In-Memory-Datenbank, lade aber keine Daten in sie und spiegele die Datenbankverbindung selbst aus. So kann ich kontrollieren, was von den Methoden execute und fetch zurückgegeben wird. Wie das geht, werden wir in diesem Abschnitt untersuchen.
Um zu lernen, wie wir Mocks in unseren Tests verwenden können, werden wir einen einzigen Test für einen gültigen Benutzer durchführen. Dieses Mal werden wir die leistungsstarke Python Mock-Bibliothek verwenden, um zu kontrollieren, was von der Verbindung zurückgegeben wird. Mock ist Teil des Moduls unittest
in Python 3. Wenn du jedoch Python 2 verwendest, musst du die Mock-Bibliothek mit pip installieren, um die neuesten Mock-Funktionen zu erhalten. Führe dazu den folgenden Befehl aus:
pip install mock
Jetzt, wo wir Mock installiert haben, können wir es in unseren Tests verwenden. Mock verfügt über eine Patch-Funktion, mit der wir ein bestimmtes Objekt in einer Python-Datei durch ein MagicMock
ersetzen können, das wir von unserem Test aus steuern können. Ein MagicMock
ist eine besondere Art von Python-Objekt, das verfolgt, wie es verwendet wird, und mit dem wir festlegen können, wie es sich verhält, je nachdem, wie es verwendet wird.
Zuerst müssen wir die Mock-Bibliothek importieren. In Python 2 müssen wir Folgendes tun:
import
mock
In Python 3 müssen wir Folgendes tun:
from
unittest
import
mock
Nachdem wir mock importiert haben, werden wir die Methode patch
als Dekorator verwenden, um den Verbindungsteil des dal
Objekts zu ersetzen. Ein Dekorator ist eine Funktion, die eine andere Funktion umhüllt und das Verhalten der umhüllten Funktion ändert. Da das Objekt dal
namentlich in die Datei app.py importiert wird, müssen wir es im Modul app
einfügen. Es wird als Argument an die Testfunktion übergeben. Jetzt, da wir ein Mock-Objekt haben, können wir einen Rückgabewert für die Methode execute
festlegen, die in diesem Fall nichts anderes als eine verkettete Methode fetchall
sein sollte, deren Rückgabewert die Daten sind, mit denen wir testen wollen. Beispiel 4-7 zeigt den Code, der benötigt wird, um das Mock anstelle des dal
Objekts zu verwenden.
Beispiel 4-7. Gespielter Verbindungstest
import
unittest
from
decimal
import
Decimal
import
mock
from
db
import
dal
,
prep_db
from
app
import
get_orders_by_customer
class
TestApp
(
unittest
.
TestCase
)
:
cookie_orders
=
[
(
u'
wlk001
'
,
u'
cookiemon
'
,
u'
111-111-1111
'
)
]
cookie_details
=
[
(
u'
wlk001
'
,
u'
cookiemon
'
,
u'
111-111-1111
'
,
u'
dark chocolate chip
'
,
2
,
Decimal
(
'
1.00
'
)
)
,
(
u'
wlk001
'
,
u'
cookiemon
'
,
u'
111-111-1111
'
,
u'
oatmeal raisin
'
,
12
,
Decimal
(
'
3.00
'
)
)
]
@mock.patch
(
'
app.dal.connection
'
)
def
test_orders_by_customer
(
self
,
mock_conn
)
:
mock_conn
.
execute
.
return_value
.
fetchall
.
return_value
=
self
.
cookie_orders
results
=
get_orders_by_customer
(
'
cookiemon
'
)
self
.
assertEqual
(
results
,
self
.
cookie_orders
)
Patching
dal.connection
imapp
Modul mit einem Mock.Dieser Mock wird als
mock_conn
an die Testfunktion übergeben.Wir setzen den Rückgabewert der Methode
execute
auf den verketteten Rückgabewert der Methodefetchall
, den wir aufself.cookie_order
setzen.Jetzt rufen wir die Testfunktion auf, in der die
dal.connection
gespottet wird und den Wert zurückgibt, den wir im vorherigen Schritt festgelegt haben.
Du siehst, dass eine komplizierte Abfrage oder ResultProxy
wie in Beispiel 4-7 schnell mühsam werden kann, wenn du versuchst, die komplette Abfrage oder Verbindung zu simulieren. Scheue dich aber nicht vor der Arbeit; sie kann sehr nützlich sein, um obskure Fehler zu finden.
Wenn du die Abfrage mockieren möchtest, würdest du nach dem gleichen Muster vorgehen, indem du den mock.patch
Dekorator verwendest und das select
Objekt im app
Modul mockst. Versuchen wir das mal mit einer der leeren Testabfragen. Wir müssen alle Rückgabewerte der verketteten Abfrageelemente, die in dieser Abfrage die Klauseln select
, select_from
und where
sind, ausspotten. Beispiel 4-8 zeigt, wie man das macht.
Beispiel 4-8. Die Abfrage auch als Mocking-out
@mock.patch
(
'
app.select
'
)
@mock.patch
(
'
app.dal.connection
'
)
def
test_orders_by_customer_blank
(
self
,
mock_conn
,
mock_select
)
:
mock_select
.
return_value
.
select_from
.
return_value
.
\
where
.
return_value
=
'
'
mock_conn
.
execute
.
return_value
.
fetchall
.
return_value
=
[
]
results
=
get_orders_by_customer
(
'
'
)
self
.
assertEqual
(
results
,
[
]
)
Mocking out der
select
Methode, da sie die Abfragekette startet.Die Dekoratoren werden der Reihe nach an die Funktion übergeben. Wenn wir von der Funktion aus den Stapel der Dekoratoren nach oben wandern, werden die Argumente auf der linken Seite hinzugefügt.
Wir müssen den Rückgabewert für alle Teile der verketteten Abfrage verspotten.
Wir müssen die Verbindung noch nachbilden, sonst würde der Code des
app
Moduls SQLAlchemy versuchen, die Abfrage zu stellen.
Als Übung solltest du den Rest der Tests, die wir mit der In-Memory-Datenbank erstellt haben, mit den gespotteten Testtypen durchführen. Ich empfehle dir, sowohl die Abfrage als auch die Verbindung zu mocken, um dich mit dem Mocking-Prozess vertraut zu machen.
Du solltest jetzt wissen, wie du eine Funktion testen kannst, die SQLAlchemy-Funktionen enthält. Du solltest auch wissen, wie du Daten in die Testdatenbank einträgst, um sie in deinem Test zu verwenden. Schließlich solltest du wissen, wie du die Abfrage- und Verbindungsobjekte als Spotting einsetzen kannst. Während in diesem Kapitel ein einfaches Beispiel verwendet wurde, werden wir in Kapitel 14, in dem wir uns mit Flask, Pyramid und pytest beschäftigen, tiefer in das Testen eintauchen.
Als Nächstes schauen wir uns an, wie wir eine bestehende Datenbank mit SQLAlchemy verwalten können, ohne das gesamte Schema in Python über reflection neu erstellen zu müssen.
Get Essential SQLAlchemy, 2. Auflage now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.