Testgetriebenen Entwicklung: In-/Export

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
MoonKid
User
Beiträge: 105
Registriert: Mittwoch 10. Dezember 2014, 16:24

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?
Benutzeravatar
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:

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
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 specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
BlackJack

@pillmuncher: Die „Note to Users“ auf der verlinkten Seite hat mich ja zu py.test wechseln lassen.
Benutzeravatar
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?
In specifications, Murphy's Law supersedes Ohm's.
MoonKid
User
Beiträge: 105
Registriert: Mittwoch 10. Dezember 2014, 16:24

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.
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.
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
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.

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>
Das würde importiert werden. Exportiere ich es wieder, sieht es natürlich etwas anders aus: z.B. der head wäre anders oder auch das Attribute bamboo-favorite würde fehlen.
MoonKid
User
Beiträge: 105
Registriert: Mittwoch 10. Dezember 2014, 16:24

Noch eine Nebenfrage: Warum sollte ich py.test, nose und Co verwenden, anstatt der Python-eigenen unittests?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Benutzeravatar
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.
MoonKid
User
Beiträge: 105
Registriert: Mittwoch 10. Dezember 2014, 16:24

Sirius3 hat geschrieben:@MoonKid: warum verwendest Du nicht eines der schon vorhandenen OPML-Pakete?
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.
BlackJack

@MoonKid: Dann würdest Du Tests für fremde Bibliotheken schreiben, was man nicht macht.
MoonKid
User
Beiträge: 105
Registriert: Mittwoch 10. Dezember 2014, 16:24

BlackJack hat geschrieben:@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.
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.
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.
Antworten