Mockito Iterator korrekt mocken

JonnyRico

Mitglied
Hi,

ich habe das Problem, dass ich das exakte Verhalten eines Iterator-Objektes nicht nachgestellt bekommen.
Hier das folgende Beispiel:

Java:
public class IteratorTest {

    @Test
    public void unmocked() {
        List<String> list = new LinkedList<String>() {
            {
                add("String 1");
                add("String 2");
            }
        };
        assertEquals("String 1String 2String 1String 2", buildString(list));
    }
    
    @Test
    public void iteratorAndListMock() {
        Iterator mockIter = mock(Iterator.class);
        when(mockIter.hasNext()).thenReturn(true, true, false);
        when(mockIter.next()).thenReturn("String 1", "String 2");
        List<String> mockList = mock(List.class);
        when(mockList.iterator()).thenReturn(mockIter);
        
        assertEquals("String 1String 2String 1String 2", buildString(mockList));
    }
    
    private String buildString(List<String> list){
        StringBuilder res = new StringBuilder();
        for (String string : list) {
            res.append(string);
        }
        for (String string : list) {
            res.append(string);
        }
        return res.toString();
    }
}

In der Methode in der ich den gemockten Iterator verwende, schlägt die Assertion fehl, da im Resultat nur "String 1String 2" steht. Die zweite For-each in buildString() wird also nicht ausgeführt bzw. der Iterator steht am Ende der Liste. Kann mir irgendjemand sagen wie man den Iterator wirklich korrekt mocken kann?
1000 Dank im Voraus.

Gruß

Jonny
 

JonnyRico

Mitglied
Hi,

ich habe jetzt noch mal ein bissel gehackt und bin zu diesem Ergebnis gekommen. Der Iterator wir quasi auf einen indizierten Zugriff umgebogen. Das ganze ist nicht schön und ich wäre auch echt sehr dankbar über eine bessere Lösung. So läuft es:

Java:
private int nextCalls = 0;
    private int hasNextCalls = 0;
    private static final String EXPECTED_RESULT = "String 1String 2String 3String 1String 2String 3String 1String 2String 3";
    List<String> list = new LinkedList<String>() {
        
        {
            add("String 1");
            add("String 2");
            add("String 3");
        }
    };
    
    @Test
    public void unmocked() {
        assertEquals(EXPECTED_RESULT, buildString(list));
    }
    
    @Test
    public void iteratorAndListMock() {        
        Iterator mockIter = mock(Iterator.class);
        
        when(mockIter.hasNext()).thenAnswer(new Answer<Boolean>() {
            
            @Override
            public Boolean answer(InvocationOnMock invocation) throws Throwable {
                ++hasNextCalls;
                if (hasNextCalls > list.size()) {
                    hasNextCalls = 0;
                    return false;
                }
                return true;
            }
        });
        when(mockIter.next()).thenAnswer(new Answer<String>() {
            
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                return list.get(nextCalls++ % list.size());
            }
        });
        List<String> mockList = mock(List.class);
        
        when(mockList.iterator()).thenReturn(mockIter);
        assertEquals(EXPECTED_RESULT, buildString(mockList));
    }
    
    private String buildString(List<String> list) {
        StringBuilder res = new StringBuilder();
        for (String string : list) {
            res.append(string);
        }
        for (String string : list) {
            res.append(string);
        }
        for (String string : list) {
            res.append(string);
        }
        return res.toString();
    }
 
G

Gelöschtes Mitglied 6946

Gast
Ich glaube, dass du von thenReturn ein anderes Verhalten erwartest. thenReturn(true, true, false) bedeutet, dass der 1. Call true liefert, der 2. Call ebenfalls und der dritte und alle weiteren Calls liefern false. Daher passiert in der zweiten for-Schleife gar nichts, da der Iterator für hasNext sofort false liefert. Das gleiche Problem gibt es bei thenReturn("String 1", "String 2"), denn da wird für den zweiten und jeden weiteren Call "String 2" zurückgeliefert.

Ich sehe da erstmal zwei Lösungsmöglichkeiten (neben deiner): Du machst ein thenReturn(true, true, false, true, true, false) daraus (analog für next) oder du erstellst dir einen zweiten Iterator-Mock mit gleichem Verhalten, den du beim zweiten iterator() Aufruf zurückgibst.
 
S

SlaterB

Gast
ohne das Framework zu kennen:
wie macht man es denn, zwischen ersten und zweiten Iterator-Aufruf zu unterscheiden?
der Code dazu ist doch anscheinend einfach nur
> when(mockList.iterator()).thenReturn(mockIter);

ok, da kann man vermuten dass die Rückgabe immer dasselbe eine Objekt ist, stattdessen bräuchte man die Rückgabe immer eines neuen Iterator-Objektes,
das ist ja ganz klar, soll nicht nur für den Test mit ZWEI Aufrufen funktionieren sondern generell beliebig oft,
wie formuliert man das, falls möglich?

> thenReturn(true, true, false, true, true, false)
steht als Lösung entsprechend völlig außer Frage, genauso eventuelles
> when(mockList.iterator()).thenReturn(mockIter, mockIter2);
 

JonnyRico

Mitglied
@SlaterB Korrekt die Lösungen einer hart kodierten Anzahl von Rückgabewerten scheidet aus. Auch meine zweite Lösung funktioniert nur genau dann, wenn die Liste immer bis zum Ende durchlaufen wird und next und hasNext damit synchron sind. Was allerdings jetzt passen sollte ich die folgende, wenn durch den permanenten Neuaufbeu einer Liste vielleicht nicht die performanteste Lösung ist diese:

Java:
private static final String EXPECTED_RESULT = "String 1String 2String 3String 1String 2String 3String 1String 2String 3";
    private static final List<String> LIST = new LinkedList<String>() {
        
        {
            add("String 1");
            add("String 2");
            add("String 3");
        }
    };
    
    @Test
    public void unmocked() {
        assertEquals(EXPECTED_RESULT, buildString(LIST));
    }
    
    
    @Test
    public void iteratorAndListMock() {
        List<String> mockList = mock(List.class);
        when(mockList.iterator()).thenAnswer(new Answer<Iterator>() {

            @Override
            public Iterator answer(InvocationOnMock invocation) throws Throwable {
                return new ArrayList<String> (LIST).iterator();
            }
        });
        
        assertEquals(EXPECTED_RESULT, buildString(mockList));
    }
    
    private String buildString(List<String> list) {
        StringBuilder res = new StringBuilder();
        for (String string : list) {
            res.append(string);
        }
        for (String string : list) {
            res.append(string);
        }
        for (String string : list) {
            res.append(string);
        }
        return res.toString();
    }
 
M

maki

Gast
Rein gefühlsmässig würde ich sagen, dass das kein Anwendungsfall für Mocks ist.

Ansonsten, das hier bitte nicht verwenden:
Java:
private static final List<String> LIST = new LinkedList<String>() {
        
        {
            add("String 1");
            add("String 2");
            add("String 3");
        }
    };
Du erstellst eine neue Implementierung der LinkedList, das willst du nicht ;)

Macht man so:
Java:
private static final List<String> list = Arrays.asList("String 1", "String 2", "String 3");
 
Zuletzt bearbeitet von einem Moderator:
Ähnliche Java Themen
  Titel Forum Antworten Datum
Robertop Mockito.when mit JDK 21 verursacht Buildfehler "RestrictedIdentifierWhen" Allgemeine Java-Themen 15
M mockito Tests Allgemeine Java-Themen 9
C Klasse mit Mockito simulieren Allgemeine Java-Themen 9
J Mockito - Objekte miteinander vergleichen (equals) Allgemeine Java-Themen 6
aze Mockito:Instanzmethode mit any simulieren Allgemeine Java-Themen 4
W Frage zu Klassendesign und Test mit Mockito Allgemeine Java-Themen 5
C Ein Iterator ist eine Implementierung des Interface Iterable? Allgemeine Java-Themen 2
D Iterator Allgemeine Java-Themen 1
M Iterator für trinären Baum Allgemeine Java-Themen 0
S OOP ClassCastException bei casting von eigener Klasse aus Iterator Allgemeine Java-Themen 3
D nested loops mit Iterator Allgemeine Java-Themen 2
Pastafari Iterator über nested HashMaps Allgemeine Java-Themen 7
A Compiler-Fehler Woher kommt der NullPointer? (Collections & Iterator) Allgemeine Java-Themen 7
Q Iterator kopieren Allgemeine Java-Themen 6
G Byte- List mit einem Iterator durchlaufen Allgemeine Java-Themen 5
DEvent Wie behandelt man Exceptions in Iterator? Allgemeine Java-Themen 2
M Iterator wirft Exception Allgemeine Java-Themen 10
G ständig Iterator über Kollektion --> falsche Reihenfolge? Allgemeine Java-Themen 2
E Iterator durchlaufen? Allgemeine Java-Themen 8
Redfrettchen addAll verwendet kein Iterator? Allgemeine Java-Themen 8
D generischer Iterator und Set Allgemeine Java-Themen 2
D generischer Iterator mit verschiedenen Typen Allgemeine Java-Themen 3
K Iterator Allgemeine Java-Themen 5
K HashMap durchlaufen mit Iterator Allgemeine Java-Themen 5
C probleme mit iterator Allgemeine Java-Themen 2
T HashCode korrekt Allgemeine Java-Themen 7
M Wie übergebe ich den Zähler für die Anzahl Rekursionsschritte korrekt? Allgemeine Java-Themen 2
D Java zeigt Buttons nicht korrekt an Allgemeine Java-Themen 0
S Text in mehreren Sprachen korrekt darstellen? Wie waehle ich die Fonts aus..? Allgemeine Java-Themen 0
Athena Programm funktioniert nur beim Debugging korrekt, sonst nicht. Allgemeine Java-Themen 1
J Datei per UDP korrekt übertragen Allgemeine Java-Themen 11
K MD5 funktioniert nicht korrekt !? Allgemeine Java-Themen 9
L Java Applet - ClassNotFound online, offline korrekt Allgemeine Java-Themen 13
M RS232 nur erste Übertragung korrekt Allgemeine Java-Themen 7
N URL einlesen -> Daten sind nicht vollständig bzw. korrekt Allgemeine Java-Themen 9
D Datum korrekt erkennen Allgemeine Java-Themen 27
G Überprüfung der Eingabe ob Typ korrekt Allgemeine Java-Themen 4
oliver1974 Wie ResourceBundles mit Unicode Zeichen korrekt einlesen? Allgemeine Java-Themen 4
G Escape-Sequenzen werden nicht korrekt umgesetzt Allgemeine Java-Themen 6
C String korrekt übergeben Allgemeine Java-Themen 8
S [Base64] Encoding von String nicht korrekt Allgemeine Java-Themen 5
M Ist das MVC korrekt? Allgemeine Java-Themen 10

Ähnliche Java Themen

Neue Themen


Oben