Ich setze mich geradem it Testgetriebener Entwicklung auseinander. Das Konzept habe ich verstanden und wie unittests technisch umgesetzt werden ist ja auch simpel. Es fällt mir aber sehr schwer, bei eigenem Code entsprechende Tests zu entwickeln.
Hier ein Feauter als Beispiel: Die Anwendung soll Daten (z.B. in Form einer XML- oder JSON-Datei) importieren und in ein eigenes internes Format verwandeln.
Ebenso soll sie dies Daten aus dem eigenen Format auch wieder exportieren können.
Wie sichere ich das mit einem Test ab? Wo sind die Dateien? Muss/Sollte ich eine Test-Daten-Datei anlegen? Oder mache ich das lieber direkt im Code. Evtl. besteht dieses Szenario aus mehreren Tests?
Testgetriebenen Entwicklung: In-/Export
- pillmuncher
- User
- Beiträge: 1484
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
Tests müssen schnell sein, sonst mag man sie nicht laufen lassen. Da IO viel Zeit braucht, sollte man ihn möglichst nicht testen. Dazu muss man die Geschäftslogik frei von IO halten und ihn ganz nach außen verschieben. Wie das geht, beschreibt Brandon Rhodes in diesem Video: https://www.youtube.com/watch?v=DJtef410XaM
In deinem Fall hast du den großen Vorteil, dass deine Basisoperationen zueinander invers sind und du sie deswegen zu einem Identitätsoperator zusammensetzen kannst. Angenommen, dein eigenes Format heißt xyz und wird in einem gleichnamigen Modul definiert, das außerdem eine Funktion from_str(some_xyz_str) beinhaltet, die aus einem String eine xyz-Struktur erstellt. Dann könntest du ungefähr sowas wie in diesem Minimalbeispiel machen:
Die ... musst du ausprogrammieren und die Testfunktionen solltest du in ein eigenes Modul auslagern. Analog zu dem Code oben kannst du XML behandeln.
Kennst du nose?
In deinem Fall hast du den großen Vorteil, dass deine Basisoperationen zueinander invers sind und du sie deswegen zu einem Identitätsoperator zusammensetzen kannst. Angenommen, dein eigenes Format heißt xyz und wird in einem gleichnamigen Modul definiert, das außerdem eine Funktion from_str(some_xyz_str) beinhaltet, die aus einem String eine xyz-Struktur erstellt. Dann könntest du ungefähr sowas wie in diesem Minimalbeispiel machen:
Code: Alles auswählen
import json
import xyz
def json_to_xyz(some_json):
...
def xyz_to_json(some_xyz):
...
def import_json(file_name):
with open(file_name, 'r') as json_file:
return json_to_xyz(json.load(json_file))
def export_json(file_name, some_xyz):
with open(file_name, 'w') as json_file:
return json.dump(xyz_to_json(some_xyz), json_file)
def test_json_to_xyz():
some_json = json.loads('...')
assert xyz_to_json(json_to_xyz(some_json)) == some_json
def test_xyz_to_json():
some_xyz = xyz.from_str('...')
assert json_to_xyz(xyz_to_json(some_xyz)) == some_xyz
Kennst du nose?
In specifications, Murphy's Law supersedes Ohm's.
@MoonKid: wenn es schon ein XML-, bzw. JSON-Format gibt, dann muß es dazu ja auch eine Spezifikation geben, wie der Aufbau dieser Formate sein soll. Soweit es geht, schreibt man für jedes einzelne Feature dieses Formats einen eigenen Test. XML und JSON sind auf Byte-Ebene nicht eindeutig, Stringvergleiche also unsinnig. Daher mußt Du die Vergleiche auf einer Ebene höher machen, also die Ausgabe nach Wörterbüchern bzw. ElementTree-Elementen testen. Bei komplexeren Daten existieren diese Funktionen ja eh, da Unterstrukturen in dieser Form zu Gesamtstrukturen zusammengesetzt werden.
@pillmuncher: Die „Note to Users“ auf der verlinkten Seite hat mich ja zu py.test wechseln lassen.
- pillmuncher
- User
- Beiträge: 1484
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@BlackJack: War mir noch nicht aufgefallen. Danke.
@Moonkid: Kennst du py.test?
@Moonkid: Kennst du py.test?
In specifications, Murphy's Law supersedes Ohm's.
Ganz dickes Danke, für deine ausführliche Beschreibung mit Beispiel. Das hilft mir schon sehr weiter und führt mich gleich zu weiteren Detail-Problemen. Auch merke ich, dass ich meine Denke stark verändern muss. Ein Konzept zu verstehen, bedeutet noch lange nicht, dass man es auch anwenden kann.pillmuncher hat geschrieben:In deinem Fall hast du den großen Vorteil, dass deine Basisoperationen zueinander invers sind und du sie deswegen zu einem Identitätsoperator zusammensetzen kannst.
Ich verstehe worauf du hinaus willst. Konkret handelt es sich um OPML. Mal ein Mini-Beispiel, weil mir immer noch nicht klar ist, wie ich das in Code umsetzen würde.Sirius3 hat geschrieben:@MoonKid: wenn es schon ein XML-, bzw. JSON-Format gibt, dann muß es dazu ja auch eine Spezifikation geben wie der Aufbau dieser Formate sein soll. Soweit es geht, schreibt man für jedes einzelne Feature dieses Formats einen eigenen Test. XML und JSON sind auf Byte-Ebene nicht eindeutig, Stringvergleiche also unsinnig. Daher mußt Du die Vergleiche auf einer Ebene höher machen
Code: Alles auswählen
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<head>
<title>Bamboo feeds</title>
</head>
<body>
<outline title="My Feeds" text="My Feeds">
<outline title="Nursing and Medicine" text="Nursing and Medicine">
<outline title="B.Sc. Nursing" text="B.Sc. Nursing" htmlUrl="" type="rss" xmlUrl="http://bscnursing.aktiv-forum.com/feed/" bamboo-favorite="false"/>
<outline title="Behindertenparkplatz" text="Behindertenparkplatz" htmlUrl="" type="rss" xmlUrl="http://www.behindertenparkplatz.de/feed/" bamboo-favorite="false"/>
</outline>
<outline title="gitarrenbeginner.de" text="gitarrenbeginner.de" htmlUrl="http://www.gitarrenbeginner.de/gitarren-tabs-lesen-lernen/" type="rss" xmlUrl="http://www.gitarrenbeginner.de/feed/atom/" bamboo-favorite="false"/>
</outline>
</body>
</opml>
@MoonKid: warum verwendest Du nicht eines der schon vorhandenen OPML-Pakete? Wenn für den Import/Export manche Attribute (bamboo-favorite) nicht relevant sind, wird ein direkter Vergleich wie pillmuncher ihn vorschlägt, nicht möglich sein. Testen würde man hier das Im-/Exportieren eines outline-Elements und getrennt davon das Exportieren des head/body.
py.test, nose und Co vereinfachen das Schreiben und Aufrufen und Auswerten der Tests.
py.test, nose und Co vereinfachen das Schreiben und Aufrufen und Auswerten der Tests.
- pillmuncher
- User
- Beiträge: 1484
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@Moonkid: Dann sowas:
Code: Alles auswählen
def expected_export_xml(import_xml):
export_xml = ... import_xml ... # transform xml import -> export manually
return export_xml
def test_xml_to_xyz():
some_xml = ... # create xml from string
assert xyz_to_xml(xml_to_xyz(some_xml)) == expected_export_xml(some_xml)
In specifications, Murphy's Law supersedes Ohm's.
In denen, welche ich bisher gesehen habe, erkenne ich keinen Vorteil. Sind auch nur xml-parser mit anderem Namen. Ich müsste genau die gleichen Tests schreiben, über die wir hier gerade rede.Sirius3 hat geschrieben:@MoonKid: warum verwendest Du nicht eines der schon vorhandenen OPML-Pakete?
@MoonKid: Dann würdest Du Tests für fremde Bibliotheken schreiben, was man nicht macht.
Logo. Kannst du evtl. intelligente OPML-Pakete empfehlen? Hab jetzt schon einige auf PiPy durchprobiert. Die machen nix, was ein xml-parser nicht auch machen würde.BlackJack hat geschrieben:@MoonKid: Dann würdest Du Tests für fremde Bibliotheken schreiben, was man nicht macht.
Ah verstehe - glaube ich. Man schreibt also nicht einen Test, für das Importieren und Exportieren. Sondern man zerlegt das Feauter "Import/Export" nochmal in Einzeltests? So kann ich es mir besser vorstellen.Sirius3 hat geschrieben:Testen würde man hier das Im-/Exportieren eines outline-Elements und getrennt davon das Exportieren des head/body.
py.test, nose und Co vereinfachen das Schreiben und Aufrufen und Auswerten der Tests.