# FileWriter / FileReader testen / Mock-Objekt für Unit Tests?



## bananenkasper (11. Sep 2010)

Hallo zusammen,

Ich habe ein paar Klassen geschrieben, um bestimmte Dateiformate zu schreiben und zu lesen.
Die Dateiformate sind alle Plain-Text, dh. die Basisklasse für alle Reader und Writer schreibt- bzw. liest einen einfachen String.

Ich teste per JUnit und möchte in den Tests nicht wirklich auf Platte schreiben, bzw. von Platte lesen.
Vielmehr würde ich lieber das Lesen-/Schreiben irgendwie mocken.

Meine Frage: *How to mock java.io.File?*
Ist das überhaupt möglich?
Was * nicht * funktioniert:

```
@Before
	public void setUp() throws Exception {
		fileMock = EasyMock.createMock(File.class);
	}

@Test
@Ignore("How to mock java.io.File?")
	public final void testWriteFile() throws IOException {
		EasyMock.expect(fileMock.getPath()).andReturn("bla");
		EasyMock.expect(fileMock.exists()).andReturn(true);
		EasyMock.expect(fileMock.canRead()).andReturn(true);
		EasyMock.expect(fileMock.isFile()).andReturn(true);
		EasyMock.replay(fileMock);
		new LazyStringWriter("test-string").write(fileMock);
		EasyMock.verify(fileMock);
	}
```

Hierbei wird logischerweise ja die Datei tatsächlich geschreiben...
Das würde ich aber gern vermeiden...

Irgendwelche Ideen?

Viele Grüße


----------



## maki (11. Sep 2010)

Interessanter wäre hier der zu testende Code(LazyStringWriter.write), wieso wird die Datei wirklich geschrieben wenn du doch nur ein Mockfile übergibst?


----------



## bananenkasper (11. Sep 2010)

```
/**
 * A {@code LazyStringWriter} provides the ability to write a string quickly to
 * <ul>
 * <li>
 * a {@link File}</li>
 * <li>
 * a {@link Writer}</li>
 * <li>
 * an {@link OutputStream}</li>
 * </ul>
 * </p>
 * <p>
 * <b>Attention:</b> writing is not buffered! If you want to write large files,
 * consider to use {@link BufferedStringWriter} instead.
 * </p>
 * <p>
 * <b>Example:</b>
 * </p>
 * <pre>
 *  @Test
	public final void example() throws IOException {
		final StringWriter wr = new StringWriter();
		new LazyStringWriter(quot;Hallo Welt!quot;).write(wr);
		assertEquals(quot;Hallo Welt!quot;, wr.toString());
	}
 * </pre>
 * 
 * @author Alexander Kerner
 * @see File
 * @see Writer
 * @see OutputStream
 * @version 2010-09-10
 * 
 */
public class LazyStringWriter implements GenericWriter {

	private final String string;

	public LazyStringWriter(Object toString) {
		this.string = toString.toString();
	}

	// Implement //

	public void write(File file) throws IOException {
		if(!FileUtils.fileCheck(file, true))
			throw new IOException("cannot access file \"" + file + "\"");
		write(new FileWriter(file));
	}

	public void write(Writer writer) throws IOException {
		StringReader reader = null;
		try {
			reader = new StringReader(string.toString());
			IOUtils.readerToWriter(reader, writer);
		} finally {
			IOUtils.closeProperly(writer);
			IOUtils.closeProperly(reader);
		}
	}

	public void write(OutputStream stream) throws IOException {
			write(IOUtils.outputStreamToWriter(stream));
	}
}
```



maki hat gesagt.:


> ..., wieso wird die Datei wirklich geschrieben wenn du doch nur ein Mockfile übergibst?



Ich bin kein IO-Experte, aber so wie ich das sehe, ist java.io.File nur ein Wrapper/Zeiger? für eine Position auf Platte.
Eigentlich nur ein String mit ein paar "extra-Funktionalitäten".

Jedenfalls zeigt mir der Mock-Test, dass auf das java.io.File Object ja nur folgende Methoden aufgerufen werden:


File.getPath();

File.exists();

File.canRead();

File.isFile();


----------



## SlaterB (11. Sep 2010)

File zu mocken, was immer das bedeutet, bringt in dem Programm tatsächlich nix, File hat doch nichts zu sagen, interessant wirds mit 

> public void write(Writer writer)
bzw. dem Aufruf
>  write(new FileWriter(file));

FileWriter ist derjenige, der die Festplatte beschmutzt, den musst du mocken, den ganzen Test so aufbauen, dass statt File ein Writer übergeben wird,
was auf die Methode
> public void write(Writer writer)
ja schon zutrifft

die andere ist bedenklich, dort vielleicht File + Writer übergeben, aber wenn ein neuer FileWriter erstellt wird kann logischerweise kaum eingegriffen werden,

---------

der ganze File-Check in der Methode,  
> if(!FileUtils.fileCheck(file, true))
usw. ist übrigens recht absurd wenn das Mock-File ungeprüft true zurückgibt,
wozu eigentlich der Test wenn letztlich die Haupt-Aktion der Methode, Schreiben auf die Festplatte, gar nicht durchgeführt wird?
dann lieber gar nicht testen.. oder willst du nur mögliche NullPointerException-Bugs jagen?


----------



## bananenkasper (11. Sep 2010)

SlaterB hat gesagt.:


> FileWriter ist derjenige, der die Festplatte beschmutzt, den musst du mocken, den ganzen Test so aufbauen, dass statt File ein Writer übergeben wird,
> was auf die Methode
> > public void write(Writer writer)
> ja schon zutrifft



Das stimmt wohl! Guter Ansatz, werde ich mal testen.

EDIT:

```
@Test
	public final void testWriteWriter() throws IOException {
		final java.io.StringWriter wr = new java.io.StringWriter();
		new LazyStringWriter(s1).write(wr);
		assertEquals(s1, wr.toString());
	}
```
Das klappt schonmal, brauche ich nicht mal zu mocken.
Aber damit habe ich ja nicht die write(java.io.File) Methode getestet...




SlaterB hat gesagt.:


> der ganze File-Check in der Methode,
> > if(!FileUtils.fileCheck(file, true))
> usw. ist übrigens recht absurd wenn das Mock-File ungeprüft true zurückgibt,
> wozu eigentlich der Test wenn letztlich die Haupt-Aktion der Methode, Schreiben auf die Festplatte, gar nicht durchgeführt wird?
> dann lieber gar nicht testen.. oder willst du nur mögliche NullPointerException-Bugs jagen?




Öh, das verstehe ich jetzt nicht so ganz. Die Klasse ist ja nicht nur zum Testen da. Der test soll "im echten Leben" gucken ob die übergebene Datei lesbar ist. Oder spielst du auf das Boolean.TRUE an? Das hat damit nix zu tun, das legt nur die Datei an, falls sie noch nicht existiert (wenn true).


----------



## SlaterB (11. Sep 2010)

> Die Klasse ist ja nicht nur zum Testen da. Der test soll "im echten Leben" gucken ob die übergebene Datei lesbar ist.

von Lesen sehe ich nicht viel, aber wenn du auf File.canRead() ansprichst na ok, 
da kann ich jetzt spontan auch nicht wirklich sagen inwieweit das zu testen ist,

bei
> public void write(Writer writer)
hast du nun aber einen Test, der auch überprüft was am Ende geschrieben rauskommt, das finde ich gut, ja in der Richtung dachte ich,
nicht dass du einen Mock übergibst der den geschrieben Text einfach verschwinden läßt (statt auf die Festplatte),

und deine Methodenbeschreibung sagt ja auch, dass in einen beliebigen Stream geschrieben wird, nicht unbedingt eine Datei,
also keine Kritik mehr


----------



## bananenkasper (11. Sep 2010)

Ja, gefällt mir auch schon ganz gut so  Also schonmal vielen Dank an dieser Stelle! :toll:

Bleibt nur noch ein Problem:
Effektiv teste ich nur eine von drei Methoden, was, je länger ich drüber nachdenke, auch OK ist, da die beiden anderen Methoden eh nur delegieren.
Aber:
So ist die Test-coverage ziemlich mickrig, das ist schlecht für die Statistik! 
Kann ich da noch was rausholen?


----------

