Methode testen die HTML generiert


#1

Hallo, genau weiß ich nicht weiter:

    private static final ArrayList<String[]> TO_PRINT = new ArrayList<>();

    private static void print(String name, Object val) {
        TO_PRINT.add(new String[]{name, val.toString()});
    }

    private static void print() {
        String n1;
        String n2 = TO_PRINT.get(0)[0];
        String n3 = TO_PRINT.get(1)[0];
        String v = TO_PRINT.get(0)[1];
        if (v.isEmpty()) {
            if (n2.equals(n3)) {
                System.out.println("<b>" + n2 + "</b><br />");
            } else {
                System.out.println("<b>" + n2 + "</b><hr />");
            }
        } else {
            if (n2.equals(n3)) {
                System.out.println("<b>" + n2 + "</b><br />" + v + "<br />");
            } else {
                System.out.println("<b>" + n2 + "</b><br />" + v + "<hr />");
            }
        }

        // HAUPTTEIL
        for (int i = 1; i < TO_PRINT.size() - 1; i++) {
            n1 = TO_PRINT.get(i - 1)[0];
            n2 = TO_PRINT.get(i + 0)[0];
            n3 = TO_PRINT.get(i + 1)[0];
            v = TO_PRINT.get(i + 0)[1];
            if (v.isEmpty()) {
                if (n2.equals(n3)) {
                    System.out.println("<br />");
                } else {
                    System.out.println("<b>" + n2 + "</b><hr />");
                }
            } else {
                if (n1.equals(n2)) {
                    if (n2.equals(n3)) {
                        System.out.println(v + "<br />");
                    } else {
                        System.out.println(v + "<hr />");
                    }
                } else if (n2.equals(n3)) {
                    System.out.println("<b>" + n2 + "</b><br />" + v + "<br />");
                } else {
                    System.out.println("<b>" + n2 + "</b><br />" + v + "<hr />");
                }
            }
        }
        // HAUPTTEIL

        n1 = TO_PRINT.get(TO_PRINT.size() - 2)[0];
        n2 = TO_PRINT.get(TO_PRINT.size() - 1)[0];
        v = TO_PRINT.get(TO_PRINT.size() - 1)[1];
        if (v.isEmpty()) {
            if (n1.equals(n2)) {
                System.out.println("<hr />");
            } else {
                System.out.println("<b>" + n2 + "</b><hr />");
            }
        } else {
            if (n1.equals(n2)) {
                System.out.println(v + "<hr />");
            } else {
                System.out.println("<b>" + n2 + "</b><br />" + v + "<hr />");
            }
        }
    }

Für ein paar Testfälle wird valides HTML geprinted. Ich weiß aber nicht ob immer.

print(name, "") -> Leerzeile im Kontext name einfügen
print(name, val) -> val im Kontext name einfügen ODER neuen Kontext name mit val einfügen, wenn nicht bereits im Kontext name

Beispiel:

        print("TABELLE", tabelle); // nur Tabelle dann hr
            // ....
            for (Item item : items) {
                print("ITEMS", item); // Item und item
            }
            print("ITEMS", ""); // br in Item
            for (int i = 0; i < 3; i++) {
                Item item = items.get(i);
                print("ITEMS", item); // item in Item
            // ....
        print("TABELLE", tabelle);
            // ....
        print("Click: " + url, ""); // nur Click dann hr
            // ....
        print("TABELLE", tabelle);
            // ....

#2

Und wo ist das Problem? Nimm ein Testframework (JUnit, TestNG) und schreib dort tests.
Du weißt ja, was du bei welcher Eingabe als Ausgabe erwartest.


#3

Das Problem ist, dass die Methode print() so viele “Verzweigungen” enthält und so lang ist, dass ich sie nicht mehr verstehe. Lapidar gesagt. :frowning:


#4

Dann mach sie eben kleiner.

Ein Teil markieren, refactor -> Extract Method und passenden Namen nehmen.

System.out.println ist wenn man automatisiert Testen möchte nicht unbedingt das geeignetste.

Du kannst der Methode auch einfach einen PrintSream übergeben und dort dann print und println aufrufen.
Und in der main-Methode übergibst du dann für den PrintStream einfach System.out.
Die andere Alternative ist String als Rückgabetyp deiner printMethode anstelle von void und System.out.println(…);

Jetzt kann man natürlich auch hingehen und das HTML abstrahieren oder man verwendet eine Templatesprache wie zum Beispiel Apache Velocity, StringTemplate, FreeMarker oder Thymeleaf.

Der Vorteil eines Templates ist, dass es eben schon sehr stark nach HTML aussieht und dann nur die Platzhalter via Model gesetzt werden müssen.

Und wenn man nun noch anstelle der < br /> für den Zeilenumbruch das ganze in ein < p > -Paragraph wrappt und einfach am Ende dann eine Horizontale Linie einzieht, dann spart man sich auch die meisten dieser Verzweigungen. Der Paragraph sorgt in der Regel automatisch für die nötigen Umbrüche.


#5

Da müsste ich mich einlesen.

Das klingt nach dem Gewünschtem.

Bis jetzt schreibe ich die Ausgabe des Java Programms in eine Datei und teste das HTML der Datei auf Validität mit einem cli tool.


#6

Generell hast du es schon richtig erkannt: die Methode ist zu komplex. Viel zu komplex. Eine einfache Faustregel nach der du gehen kannst: Wenn du einen Einzeiler-Kommentar in deiner Methode drin irgendwo ranpacken willst, dann kannst du das auch direkt als Methode auslagern.

void demo() {
      ...
     // Create customer account
     ...
}

Anstelle von Create customer account könntest du hier eine Methode erstellen die createCustomerAccount heißt. Die kann dokumentiert sein + kann einzeln getestet werden.


Ansonsten wie ionutbalu schon gesagt hat: System.out.println ist keine gute Idee. Gib das Ergebnis zurück und auch hier gilt: wähle den kleinsten gemeinsamen Nenner: string. Klar könntest du einen PrintStream übergeben - aber dann wäre das Ergebnis auf den Stream limitiert. Es gibt keinen Grund für diese Limitierung. Das ganze als String zurückgeben sorgt dafür, dass die Methode eine höhere Wiederverwendbarkeit aufweist.


Lohnt sich. Das würde es dir erheblich vereinfachen.


#7

Mache ich jetzt auch, danke dafür. :slight_smile:


Ansonsten wird die Ausgabe in eine Datei umgeleitet, das HTML überprüft und aus dem HTML ein PDF erstellt. Das sieht dann (wieder nach Grafik umgewandelt) zum Bleistift so aus:

0002 - Kopie

Im unteren Drittel sieht man, dass unter Items ein Zeilenumbruch eingefügt wurde, und die Zeilenumbrüche auch richtig gesetzt wurden.


Was würdet ihr davon halten?:
http://www.vogella.com/tutorials/Logging/article.html


Hier mit funktioniert auch so einiges:

import org.pmw.tinylog.Logger;

Configurator.defaultConfig().formatPattern("{date:yyyy-MM-dd HH:mm:ss}: {message}\\n").writer(new FileWriter("ausgabe.txt")).level(Level.DEBUG).activate();

Logger.info(item); // Beispiel
Logger.error(e, "IOException | ClassNotFoundException was thrown");

Das ist nicht so umfangreich wie log4j… Wie man sieht hab ich mit \\n jeweils neue Zeile.

Eigentlich könnte man dann auch HTML-TAGs drumherum machen. Dann ist aber noch nicht ~“message s für Kategorien in Kategorien zusammengefasst”.


#8

Bin nicht ganz sicher, ob ich das richtig gemacht hab:

tipsy/j2html :

import static j2html.TagCreator.*;
import j2html.tags.ContainerTag;
// ....

    private static ContainerTag body = body();
    private static ContainerTag div;

    private static void addNameToDivTag(String n) {
        if (div != null) {
            body = body.with(div.with(br()));
        }
        div = div().with(join(b(n), ":"));
    }

    private static void addValueToDivTag(Object o) {
        div.with(join(o.toString(), br()));
    }

    private static String getDivTag() {
        body = body.with(div);
        return body.renderFormatted();
    }

.renderFormatted() liefert auf jeden Fall wohlgeformtes, valides, hübsches HTML zurück…
addNameToDivTag() startet sozusagen eine neue Überschrift,
addValueToDivTag() fügt einen Wert zu der sozusagen bereits Überschrift hinzu.

(leider verwenden die render-Methoden Stringjoins und diese gibt’s erst in Java 8 :frowning: deswegen kann ich j2html nicht verwenden)


#9

Problem: table oder strukturierte Blöcke sind innerhalb von p-Tags nicht erlaubt.