Unit Testing mit vielen Bedingungen - Best Practice?

JuriW

Aktives Mitglied
Ich bin ein Anfänger bezüglich Unit-Tests. Ich würde gerne Unit Tests für viele Key Value Paare schreiben, von denen die Values oftmals gleiche Bedingungen haben. Beispiel:
Ich habe viele kleine String Arrays, wobei der erste Wert ein Key ist und der zweite Wert ein Value (Ja, ich weiß, dass es bessere Implementierungen gibt).
Java:
public static void main(String[] args) {

    String[] kv1 = {"se", "Server is running"};
    String[] kv2 = {"ap", "App is running"};
    String[] kv3 = {"pr", "Printer is running"};
    String[] kv4 = {"ra", "Radio is running"};
    String[] kv5 = {"ws", "Website is not available"};
    /*
     *
     * weitere kv-paare
     *
     */
    ArrayList<String[]> pairs = new ArrayList<>(Arrays.asList(kv1, kv2, kv3, kv4, kv5));
    for (String[] kv : pairs) {
        isValueValid(kv);
    }
}

public static boolean isValueValid(String[] pair) {

    if (pair[0].equals("se") || pair[0].equals("ab") || pair[0].equals("pr")) {
        if (pair[1].length() > 50) {
            System.out.println("Value is shouldn't contain more than 50 chars");
            return false;
        }
    } else if (pair[0].equals("ra") || pair[0].equals("ws")) {
        if (pair[1].length() > 100) {
            System.out.println("Value is shouldn't contain more than 100 chars");
            return false;
        }
    }
    /*
     *
     * mehr Bedingungen für andere kv-paare
     *
     */
    return true;
}

Die Sache ist, dass das CI System nur den Methodennamen von dem Test ausgibt, wenn er fehlt schlägt.

Heißt, ich muss jeden key als einzelnen Test, mit sprechendem Namen schreiben:
Java:
 private String[] pair = new String[2];

    @Test
    public void isServerValueValid(){
        pair[0] = "se";
        int criteria = 50; // defines maximum length

        pair[1] = "";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = "abcdef";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria);
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria+1);
        assertEquals(false, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria + 20);
        assertEquals(false, TestKlasse.isValueValid(pair));

    }

    @Test
    public void isAppValueValid(){
        pair[0] = "ab";
        int criteria = 50; // defines maximum length

        pair[1] = "";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = "abcdef";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria);
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria+1);
        assertEquals(false, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria + 20);
        assertEquals(false, TestKlasse.isValueValid(pair));
    }

    @Test
    public void isPrinterValueValid(){
        pair[0] = "pr";
        int criteria = 50; // defines maximum length

        pair[1] = "";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = "abcdef";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria);
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria+1);
        assertEquals(false, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria + 20);
        assertEquals(false, TestKlasse.isValueValid(pair));
    }

    @Test
    public void isRadioValueValid(){
        pair[0] = "ra";
        int criteria = 100; // defines maximum length

        pair[1] = "";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = "abcdef";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria);
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria+1);
        assertEquals(false, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria + 20);
        assertEquals(false, TestKlasse.isValueValid(pair));
    }

    @Test
    public void isWebsiteValueValid(){
        pair[0] = "ws";
        int criteria = 100; // defines maximum length

        pair[1] = "";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = "abcdef";
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria);
        assertEquals(true, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria+1);
        assertEquals(false, TestKlasse.isValueValid(pair));
        pair[1] = generateValueData(criteria + 20);
        assertEquals(false, TestKlasse.isValueValid(pair));
    }

    private String generateValueData(int length){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < length; i++){
            sb.append("a");
        }
        return sb.toString();
    }


Meine Fragen sind nun:
1. Ist das der richtige Ansatz zum testen?
2. Wie kann ich in meinem Code in der Funktion isValueValid die if-else if-Blöcke schöner gestalten?
 

httpdigest

Top Contributor
Die isValueValid() Methode könntest du vereinfachen, indem du die Entscheidungen, wann ein Wert zu lang ist, in eine Map-Datenstruktur kodierst:
Java:
private static final Map<String, Integer> MAX = Map.of(
    "se", 50,
    "ab", 50,
    "pr", 50,
    "ra", 100,
    "ws", 100
);
public static boolean isValueValid(String[] pair) {
  return !MAX.containsKey(pair[0])
       || MAX.get(pair[0]) >= pair[1].length();
}
(Utility-Methoden sollten bevorzugt nichts auf stdout/stderr ausgeben. Hier könntest du eher mit Exceptions arbeiten.)
Außerdem: Warum verwendest du ein String[] Array und nicht einfach zwei String-Parameter?

Bezüglich Unit-Test:
Google mal nach "<dein-unit-framework> parameterized tests".
Für JUnit 4 etwa: https://github.com/junit-team/junit4/wiki/parameterized-tests
oder TestNG: https://testng.org/doc/documentation-main.html#parameters-dataproviders
 

JuriW

Aktives Mitglied
An die HashMap dachte ich auch schon. Das Problem ist aber, dass Kriterien nicht nur aus Zahlen bestehen, sondern zum Beispiel auch aus einem Zahlenbereich (50-100) oder nur aus bestimmten, einzelnen Buchstaben bestehen dürfen. Hast du dafür auch noch eine Idee?

Und danke für die Links! Mein Framework ist JUnit, wahlweise hamcrest oder AssertJ

Warum verwendest du ein String[] Array und nicht einfach zwei String-Parameter?
Ich kriege in der Funktion vor meiner eigentlichen Funktion die Parameter als String[]. Ich hab es einfach mal so beibehalten.
 

httpdigest

Top Contributor
An die HashMap dachte ich auch schon. Das Problem ist aber, dass Kriterien nicht nur aus Zahlen bestehen, sondern zum Beispiel auch aus einem Zahlenbereich (50-100) oder nur aus bestimmten, einzelnen Buchstaben bestehen dürfen. Hast du dafür auch noch eine Idee?
Du kannst die Bedingungen als Predicate<String> abbilden. Dann kannst du dir für jede Bedingung/Prüfung eine parametrisierte Methode schreiben, die diese Prüfung ausdrückt. Z.B.:
Java:
import java.util.*;
import java.util.function.*;
...
private static final Map<String, Predicate<String>> CONDITIONS = Map.of(
    "ws", maxLength(100),
    "xx", rangeLength(10, 20),
    "yy", containsOnly('a', 'b', 'x') // <- characters MUST be in ascending order!
);
private static Predicate<String> maxLength(int length) {
  return s -> s.length() <= length;
}
private static Predicate<String> rangeLength(int min, int max) {
  return s -> s.length() >= min && s.length() <= max;
}
private static Predicate<String> containsOnly(char... chars) {
  return s -> s.chars().allMatch(c -> Arrays.binarySearch(chars, (char) c) > -1);
}
public static boolean isValueValid(String[] pair) {
  return !CONDITIONS.containsKey(pair[0])
       || CONDITIONS.get(pair[0]).test(pair[1]);
}
 

JuriW

Aktives Mitglied
Java:
import java.util.*;
import java.util.function.*;
...
private static final Map<String, Predicate<String>> CONDITIONS = Map.of(
    "ws", maxLength(100),
    "xx", rangeLength(10, 20),
    "yy", containsOnly('a', 'b', 'x') // <- characters MUST be in ascending order!
);
private static Predicate<String> maxLength(int length) {
  return s -> s.length() <= length;
}
private static Predicate<String> rangeLength(int min, int max) {
  return s -> s.length() >= min && s.length() <= max;
}
private static Predicate<String> containsOnly(char... chars) {
  return s -> s.chars().allMatch(c -> Arrays.binarySearch(chars, (char) c) > -1);
}
public static boolean isValueValid(String[] pair) {
  return !CONDITIONS.containsKey(pair[0])
       || CONDITIONS.get(pair[0]).test(pair[1]);
}

Ist das eine Java 9 Lösung? Gibt es auch eine Java 8 Lösung?

EDIT: Sorry, habs rausgefunden:

Java:
 private static final HashMap<String, Predicate<String>> CONDITIONS = new HashMap<String, Predicate<String>>(){
        {
            put("ws", maxLength(100));
            // usw
        };
   
};
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
EDIT: Sorry, habs rausgefunden:
Generell dazu: http://errorprone.info/bugpattern/DoubleBraceInitialization


Ist das eine Java 9 Lösung? Gibt es auch eine Java 8 Lösung?
Java:
private static final Map<String, Predicate<String>> CONDITIONS = createConditions();
private static createConditions() {
    final Map<String,Predicate<String>> conditions = new HashMap<>();
    conditions.put("ws", maxLength(100));
    conditions.put("xx", rangeLength(10, 20));
    conditions.put("yy", containsOnly('a', 'b', 'x')); // <- characters MUST be in ascending order!
    return conditions;
}

oder auch
Java:
private static final Map<String, Predicate<String>> CONDITIONS = new HashMap<>();
static {
    CONDITIONS.put("ws", maxLength(100));
    CONDITIONS.put("xx", rangeLength(10, 20));
    CONDITIONS.put("yy", containsOnly('a', 'b', 'x')); // <- characters MUST be in ascending order!
}
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
cowabunga1984 Unit-Testing - Welche Testfälle sind relevant? Java Basics - Anfänger-Themen 4
neerual Klassen Wie rufe ich Klassen, die andere Klassen extenden in einer Test Unit auf? Java Basics - Anfänger-Themen 10
M Open Source Projekt mit Unit Tests gesucht Java Basics - Anfänger-Themen 5
U Best Practice Datenbereitstellung Unit Test Java Basics - Anfänger-Themen 6
T Unit tests fehlerhaft bitte um hiiiiilfe :D Java Basics - Anfänger-Themen 1
J J-Unit Tests Java Basics - Anfänger-Themen 6
V Mediaplayer - NullPointerException bei Unit-Test Java Basics - Anfänger-Themen 4
A JUnit testing is inkonsistent Java Basics - Anfänger-Themen 12
W Testing Tipps Java Basics - Anfänger-Themen 3
L Junit Testing bei XML? Java Basics - Anfänger-Themen 3
F Erste Schritte Glass-Box-Testing ->Bedingungsüberdeckung Java Basics - Anfänger-Themen 1
J WhiteBox-Testing Java Basics - Anfänger-Themen 2
X JUnit testing Java Basics - Anfänger-Themen 7
L JUNIT Testing Java Basics - Anfänger-Themen 3
M Arrayliste mit beliebig vielen Namen befüllen Java Basics - Anfänger-Themen 4
F HttpURLConnection mit vielen Parametern Java Basics - Anfänger-Themen 3
W Geodaten API mit vielen zusätzlichen Infos Java Basics - Anfänger-Themen 4
P Taschenrechner mit unendlich vielen Eingabemöglichkeiten Java Basics - Anfänger-Themen 1
V Erste Schritte Taschenrechner mit beliebig vielen Zahlen Java Basics - Anfänger-Themen 5
B Datenbank: Entity mit vielen Referenzen? Ansatz so ok? Java Basics - Anfänger-Themen 8
JDimi Textdatei mit beliebig vielen Zeilenumbrüchen erstellen Java Basics - Anfänger-Themen 2
B Schreiben von zu vielen Einträgen in einer Datenbank Java Basics - Anfänger-Themen 9
M Input/Output Arbeiten mit extrem vielen Dateien Java Basics - Anfänger-Themen 8
K Ein zentrales Objekt in vielen anderen Klassen nutzen? Java Basics - Anfänger-Themen 22
B Input/Output File.length() gibt bei vielen Ordnern 0 zurück Java Basics - Anfänger-Themen 9
P Wie reagiere ich auf Strings mit zu vielen Chars ? Java Basics - Anfänger-Themen 6
S Variablen Prüfen, ob einer von vielen boolean true ist Java Basics - Anfänger-Themen 8
H Die Eingabe von beliebig vielen Zahlen in ein Array - ich kann es nicht Java Basics - Anfänger-Themen 6
M aus vielen backsalshs einen machen Java Basics - Anfänger-Themen 6
V ActionListener bei vielen Modulen Java Basics - Anfänger-Themen 3
Luk10 Problem mit vielen Timer(-Events) Java Basics - Anfänger-Themen 17
L Arrays mit unbestimmt vielen Feldern Java Basics - Anfänger-Themen 7
K ArrayListe erstellen mit vielen (unendlichen) Weren ??? Java Basics - Anfänger-Themen 29
K 50 errormeldungen- komme auch nach vielen Nachmittagen nicht weiter. bitte helfen Java Basics - Anfänger-Themen 39
G Verzeichnis auslesen mit sehr sehr vielen Bildern Java Basics - Anfänger-Themen 6
N Herangehensweise - mit vielen Variablen arbeiten Java Basics - Anfänger-Themen 6
C Gleichbehandlung von vielen jTextFields Java Basics - Anfänger-Themen 11
B Einen Array aus vielen Vectoren Java Basics - Anfänger-Themen 3
W sleep "hängt" bei vielen Threads Java Basics - Anfänger-Themen 2

Ähnliche Java Themen

Neue Themen


Oben