Java InputStream verspotten

74

Bitte geben Sie Zeiger an, damit ich das Java InputStream-Objekt verspotten kann. Dies ist die Codezeile, die ich verspotten möchte:

InputStreamReader inputData = new InputStreamReader(System.in);
bufferdReader = new BufferedReader(inputData);
bufferdReader.readLine(); 
Reji
quelle
1
Ist Ihr Problem das System.in? Sie möchten Ihren Code wahrscheinlich so umgestalten, dass anstelle von System.in ein anderer InputStream übergeben wird. Dann können Sie ein beliebiges Mocking-Framework (wie in den Antworten erwähnt) verwenden, um diesen InputStream zu verspotten.
Phtrivier

Antworten:

58
BufferedReader bufferedReader = org.mockito.Mockito.mock(BufferedReader.class);
when(bufferedReader.readLine())
  .thenReturn("first line")
  .thenReturn("second line");

org.junit.Assert.when(new Client(bufferedReader).parseLine())
  .thenEquals(IsEqual.equalTo("first line"));
Boris Pavlović
quelle
Vielen Dank für die schnelle Antwort .. werde es überprüfen
Reji
Wie importiere ich Client? Es tut mir leid, aber ich bin neu in Mockito und Junit. Sie verwenden Client in dieser Zeile:org.junit.Assert.when(new Client(bufferedReader).parseLine())
Alejandro Sanchez
Client ist eine Klasse, die den gepufferten Leser verwendet, um Zeile für Zeile zu lesen und zu analysieren
Boris Pavlović
109

Sie können commons-io verwenden , um einige Stub-Eingabestreams zu erstellen:

InputStream stubInputStream = 
     IOUtils.toInputStream("some test data for my input stream", "UTF-8");
Eric
quelle
1
Wenn Sie den Stream mehrmals verwenden möchten, wie würden Sie ihn nach der Verwendung zurücksetzen?
Exic
Sie können es in einen BufferedInputStream einbinden und mark () und reset () verwenden. Aber wahrscheinlich ist es einfacher, eine neue in Ihrer Setup-Methode zu instanziieren (vorausgesetzt, wir sprechen hier noch über das Testen).
Eric
1
seit v2.3 müssen Sie den Zeichensatz hinzufügen, zum Beispiel : toInputStream("someString", "UTF-8"). Die Version mit nur String als Parameter ist veraltet.
JonyD
Danke @JonyD! Ich habe die Antwort aktualisiert, um den Zeichensatzparameter einzuschließen.
Eric
Meine Lieblingsantwort! Ich würde nur hinzufügen, dass Sie anstelle der nicht typsicheren ZeichenfolgeStandardCharsets.UTF_8
dcasadevall
96

Sie können einfach a verwenden ByteArrayInputStreamund es mit Ihren Testdaten füllen.

@ Brads Beispiel aus den Kommentaren:

InputStream anyInputStream = new ByteArrayInputStream("test data".getBytes());
Brei
quelle
Zusätzlicher Vorteil ist, dass ByteArrayInputStreamunterstützt mark()undreset()
Hank
4
Schön und einfach ...InputStream anyInputStream = new ByteArrayInputStream("test data".getBytes());
Brad
20

Ich bin mit der ausgewählten Antwort auf diese Frage nicht einverstanden. Mocking-Frameworks wie Mockito sind nett und alle, aber wenn Standard-Java-API verfügbar ist, können Sie dies stattdessen in Betracht ziehen.

dh

BufferedReader reader = new BufferedReader(new StringReader("some string"));

Warum sollten Sie in Ihren Testklassen ein Mock-Objekt verwenden, wenn Sie ein reales Objekt mit all seinem Status und Verhalten verwenden könnten?

Um mehr darüber zu erfahren, wie dies funktioniert, können Sie das Designmuster "Dekorateur" nachschlagen.

John Deverall
quelle
3
Hier ist ein Anwendungsfall: Verspotten Sie mehrere verschiedene Aufrufe, z. B. in einer Schleife (falsche Eingabe1 -> falsche Eingabe2 -> richtige Eingabe3).
Jan Groth
Behalten Sie Ihren InputStream-Code und Ihren Reader-Code in separaten Klassen, genau wie die Java-API. Lassen Sie Ihre Testklasse eine Implementierung von Reader und nicht InputStream bestehen und übergeben Sie im Produktionscode auch eine Implementierung von Reader an die Klasse, die Ihre Daten liest / Ihre Datenverarbeitung durchführt.
John Deverall
3

Ändern Sie Ihr Objekt so, dass es einfacher zu testen ist.

public MyObject {
    private InputStream inputStream;

    public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}

    public void whatever() {
        InputStreamReader inputData = new InputStreamReader(inputStream);
        bufferdReader = new BufferedReader(inputData);
        bufferdReader.readLine(); 
    }
}

Wenn Sie dann Ihr Objekt verwenden, initialisieren Sie zuerst seinen inputStream:

MyObject myObject = new MyObject();
myObject.setInputStream(System.in);

Jetzt haben Sie ein Objekt, mit dem Sie es mit einer beliebigen Implementierung von InputStream testen können (ByteArrayInputStream ist ein guter Versuch).

Nathan Hughes
quelle
2
@Test
    public void testReadFile() {
    TestClass ClassName = Mockito.mock(TestClass.class);
     InputStream in = Mockito.mock(InputStream.class);
     InputStreamReader inr =Mockito.mock(InputStreamReader.class);
     BufferedReader bufferedReader =Mockito.mock(BufferedReader.class);
       try {
         PowerMockito.whenNew(InputStreamReader.class).withArguments(in).thenReturn(inr);
         PowerMockito.whenNew(BufferedReader.class).withArguments(inr).thenReturn(bufferedReader);
         String line1 = "example line";
         PowerMockito.when(bufferedReader.readLine()).thenReturn(line1).thenReturn(null);
         method return type = Whitebox.invokeMethod(ClassName, "MethodName", arguement);
         assertEquals("result is::","expected", actual);
     } catch (Exception e) {
         e.printStackTrace();
     }
 }
Pentayya
quelle
2
String testString = "test\nstring";
InputStream stream = new ByteArrayInputStream(testString.getBytes(StandardCharsets.UTF_8));

BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

Assert.assertEquals("test", reader.readLine());
Assert.assertEquals("string", reader.readLine());
eyveer
quelle
1

Die beste Lösung, die ich gefunden habe, ist die Verwendung

final InputStream inputStream1 = IOUtils.toInputStream("yourdata");

und wickeln Sie dann den Inpustream in bufferedReader ein, um den Test am besten um den Eingabestream zu schreiben

Pritesh
quelle
1
Dies fügt eine weitere Abhängigkeit von IOUtils hinzu, wodurch Ihr Testcode größer wird. Nicht der gewünschte Effekt, den wir von kleinen Unit-Tests
erwarten
0

Angenommen, Sie verwenden Maven, können Sie eine Ressource in den Ordner "src / test / resources /" legen, beispielsweise "src / test / resources / wunderbar-mock-data.xml ". Dann können Sie in Ihnen Folgendes tun:

    String resourceInputFile = "/database-insert-test.xml";
    URL url = this.getClass().getResource(resourceInputFile);
    Assert.assertNotNull("Can't find resource " + resourceInputFile, url);

    InputStream inputStream = url.openStream();

    // Now you can just use the inputStream for method calls requiring this param
    (...)

In diesem Beispiel ist die URL varialble null, wenn die angegebene Ressource im aktuellen Klassenpfad nicht gefunden werden kann. Mit diesem Ansatz können Sie mehrere Szenarien in verschiedene resourceInputFile (s) einfügen ... Denken Sie auch daran, dass alle Arten von Ressourcen unter "src / test / resources /" (nicht nur XML-Dateien, wie z. B. txt, html, jpeg usw.) gespeichert sind. ) sind normalerweise als Klassenpfadressourcen aus allen jUnit-Tests verfügbar.

A. Masson
quelle