Tests for a method that writes to a file in Java - java

I have a method that writes to a file with a given name. Do I have to test this method and, if so, for what should I test it?
public void record(Object o){
try{
FileWriter fileStream = new FileWriter("data.txt", true);
BufferedWriter out = new BufferedWriter(fileStream);
out.write(o.toString());
out.newLine();
out.close();
} catch (Exception e){
System.err.println("Error: " + e.getMessage());
}
}

Whether you have to test is or not is a question for your project lead. If it's determined you should write some tests, then I would test at least these cases
Happy path (write a good object, check the file contents afterwards)
What happens with a null argument (file exist? what's in it? Does it get closed?)
What happens if the method is called multiple times?
What happens if the file already exists, but is not writeable

The answer to the question "Should I test this?" is always "YES". Now, you might not be able to, or might not know how to, or have the time to, or be allowed to, or want to, but that doesn't change the answer.

Tests are always welcome, but in this case thorough code review might be beneficial. It is hard to write a test that will discover that:
out might not be closed properly, causing file descriptors to leak, i.e. when o.toString() throws an exception
if it throws an exception, the stack trace will get lost (incorrect exception handling)
how should the method behave if the Object o does not override toString() should it care?

A couple more code review suggestions: out.close() should be in a finally block (with its own try/catch), you might want to check the input parameter for sanity, you might want to check if the file already exists and is writable. Also, what happens if o is a String with its own newlines?

Related

Why we need to handle or throw checked exceptions in Java?

I am new to Java, I read that checked exception get raised during compile time only i.e. program will not compile successfully if there is a checked exception which is not handled or thrown.
If something is preventing a compiler to compile a code, then we could just erase it or recode it in another way so that problem doesn't exist.
For example, if we are trying to open a file which is not there in system, we should not just open it. So why the need to handle/throw these exceptions ?
Your conceptual problem here is that you are conflating what happens at compile time and what happens at runtime; i.e. when the program is compiled by the programmer and when it is run by the user.
At compile time the compiler analyses the program to determine what exceptions could be thrown. For example
public static void main(String[] args) {
FileInputStream fis = new FileInputStream(args[0]); // HERE
}
The FileInputStream(String) constructor is declared as throws IOException. (Look it up.) So the compiler knows that the statement at HERE could throw an IOException. And IOException is a checked exception. (Look it up.)
It doesn't know that it will. It cannot possibly know that it will ... because it doesn't know what args[0] will contain. That is only known at runtime; i.e. when the program is run and the user supplies some command line arguments.
Q: What does checked exception mean here?
Well it means the main method either has to be declared as (for example) throws IOException, or it must catch it in a try-catch statement.
Q: So why is is a checked exception?
Because it was declared that way!
Q: Why was it declared that way?
To force the programmer to do deal with the possibility that the file being opened does not exist, is not readable, and so on. When the program is (eventually) run.
The compiler is saying "do something about this thing that might happen ...".
Just to reiterate. The compiler cannot check that the file exists because it doesn't know what pathname the user is going to provide. And even if it did know, AND it checked1 that the file existed at compile, it couldn't know if the file was going to still exist at runtime, possibly on a completely different machine on a different network ... many years in the future.
1 - This is hypothetical. It doesn't check. It would be pointless.
You shouldn't see exception handling as a problem but as a feature.
Assume exceptions wouldn't exist.
var file = new File("test.txt");
if (!file.exists()) {
file.createNewFile();
}
var writer = new FileWriter(file);
// ...
What could go wrong?
Between the check whether the file exists and opening the reader, the file might have been removed by another thread/process. So even though you created it, it's gone -> you need to somehow lock the file
Your memory is full, hence the file could not be created -> you need to check the result of createNewFile for that.
The file exists, but is a directory.
The file is locked, because another process is writing to it -> You need to check if it is being written to.
This would do it (still assuming no exceptions):
var file = new File("test.txt");
if (!file.exists()) {
if(file.createNewFile()) {
if (!file.isDirectory()) {
if (!isUsed(file)) {
var writer = new FileWriter(file);
// ...
}
}
}
}
This is a lot of code and still doesn't handle the first problem.
Whereas
var file = new File("test.txt");
try {
var writer = new Filewriter(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
is way shorter, clearer, easier to understand.
Also, usually, it is more likely that everything works as supposed that that any of these problems occur. So instead of assuming all the worst and doing multiple checks beforehand, one just assumes the best and if something fails, you look for the reason.
This also impacts the runtime. If you run the no-exception code 1000 times, all these checks will be run a 1000 times, no matter if they fail or not. For the exception-code this is not the case, the might never be run at all.
Checked exception in Java have to be handled, that is why the compiler will complain about and didn't compile the code.
But the exception itselfs will not be raised until runtime, in case it happens.
When you write your code you should just handle properly all your checked exception, so you have to write a try catch block or just return the exception from the method.
If you use some library that raises checked exception you can just handle in one of the 2 ways I have already explained.
But in your code you can choose to use unchecked exceptions.
Those kind of exceptions can be ignored and the compiler will be just fine.
Of course your app will crash if one of those unchecked exception is raised while execution and it is not caught.
But that can be desirable in certain situation, where there is no right way to handle the Exception and it is generally a subclass of Error.
Anyway you should not think about on how to handle the error cases in your code but only Exceptions.
More: Exception class, Error class

What is the use case for null(Input/Output)Stream API in Java?

With Java 11, I could initialize an InputStream as:
InputStream inputStream = InputStream.nullInputStream();
But I am unable to understand a potential use case of InputStream.nullInputStream or a similar API for OutputStream
i.e. OutputStream.nullOutputStream.
From the API Javadocs, I could figure out that it
Returns a new InputStream that reads no bytes. The returned stream is
initially open. The stream is closed by calling the close() method.
Subsequent calls to close() have no effect. While the stream is open,
the available(), read(), read(byte[]), ...
skip(long), and transferTo() methods all behave as if end of stream
has been reached.
I went through the detailed release notes further which states:
There are various times where I would like to use methods that require
as a parameter a target OutputStream/Writer for sending output, but
would like to execute those methods silently for their other effects.
This corresponds to the ability in Unix to redirect command output to
/dev/null, or in DOS to append command output to NUL.
Yet I fail to understand what are those methods in the statement as stated as .... execute those methods silently for their other effects. (blame my lack of hands-on with the APIs)
Can someone help me understand what is the usefulness of having such an input or output stream with a help of an example if possible?
Edit: One of a similar implementation I could find on browsing further is apache-commons' NullInputStream, which does justify the testing use case much better.
Sometimes you want to have a parameter of InputStream type, but also to be able to choose not to feed your code with any data. In tests it's probably easier to mock it but in production you may choose to bind null input instead of scattering your code with ifs and flags.
compare:
class ComposableReprinter {
void reprint(InputStream is) throws IOException {
System.out.println(is.read());
}
void bla() {
reprint(InputStream.nullInputStream());
}
}
with this:
class ControllableReprinter {
void reprint(InputStream is, boolean for_real) throws IOException {
if (for_real) {
System.out.println(is.read());
}
}
void bla() {
reprint(new BufferedInputStream(), false);
}
}
or this:
class NullableReprinter {
void reprint(InputStream is) throws IOException {
if (is != null) {
System.out.println(is.read());
}
}
void bla() {
reprint(null);
}
}
It makes more sense with output IMHO. Input is probably more for consistency.
This approach is called Null Object: https://en.wikipedia.org/wiki/Null_object_pattern
I see it as a safer (1) and more expressive (2) alternative to initialising a stream variable with null.
No worries about NPEs.
[Output|Input]Stream is an abstraction. In order to return a null/empty/mock stream, you had to deviate from the core concept down to a specific implementation.
I think nullOutputStream is very easy and clear: just to discard output (similar to > /dev/null) and/or for testing (no need to invent an OutputStream).
An (obviously basic) example:
OutputStream out = ... // an easy way to either print it to System.out or just discard all prints, setting it basically to the nullOutputStream
out.println("yeah... or not");
exporter.exportTo(out); // discard or real export?
Regarding nullInputStream it's probably more for testing (I don't like mocks) and APIs requiring an input stream or (this now being more probable) delivering an input stream which does not contain any data, or you can't deliver and where null is not a viable option:
importer.importDocument("name", /* input stream... */);
InputStream inputStream = content.getInputStream(); // better having no data to read, then getting a null
When you test that importer, you can just use a nullInputStream there, again instead of inventing your own InputStream or instead of using a mock. Other use cases here rather look like a workaround or misuse of the API ;-)
Regarding the return of an InputStream: that rather makes sense. If you haven't any data you may want to return that nullInputStream instead of null so that callers do not have to deal with null and can just read as they would if there was data.
Finally, these are just convenience methods to make our lifes easier without adding another dependency ;-) and as others already stated (comments/answers), it's basically an implementation of the null object pattern.
Using the null*Stream might also have the benefit that tests are executed faster... if you stream real data (of course... depending on size, etc.) you may just slow down your tests unnecessarily and we all want tests to complete fast, right? (some will put in mocks here... well...)

Java 'finally' exception throwing rules

Lets say I have a class that has two OutputStream members:
public class A {
OutputStream os1 = ...
OutputStream os2 = ...
...
}
Now, in this class, I have a cleanup() method which must at least attempt to close both streams:
public void cleanup() throws IOException {
try {
os1.close();
} finally {
os2.close();
}
}
Now, I'm confident that the above code will try to close both streams, but what is interesting is what happens when both calls to close() throw an IOException. I want to say that the exception from os2 will propogate upward, but perhaps the behavior is undefined. I've done some googling but I'm not finding anything that answers this question well.
The JLS §14.20.2 describes the behaviour.
The exception, that is thrown by the method is the exception caused by os2.close().
If the finally throws an exception, then the method terminates with that exception. It's very well defined behavior. Likewise, if a finally returns, then the method returns with the finally's return value, regardless of any previous return from the associated try block.
Yes, the second exception is propagated upwards, and the first exception is lost.
But if you're using Java 7 or better, it's far better to use the "try with resources" syntax. The code is much cleaner, there's a lot less to write, and it'll do a far better job than you can of making sure the resources get closed when they should, no matter what. For example, no matter how smart and fancy you make cleanup(), if it doesn't get called then you're out of luck. What if the context that's supposed to call it itself throws an exception? Don't do it that way!

Should java try blocks be scoped as tightly as possible?

I've been told that there is some overhead in using the Java try-catch mechanism. So, while it is necessary to put methods that throw checked exception within a try block to handle the possible exception, it is good practice performance-wise to limit the size of the try block to contain only those operations that could throw exceptions.
I'm not so sure that this is a sensible conclusion.
Consider the two implementations below of a function that processes a specified text file.
Even if it is true that the first one incurs some unnecessary overhead, I find it much easier to follow. It is less clear where exactly the exceptions come from just from looking at statements, but the comments clearly show which statements are responsible.
The second one is much longer and complicated than the first. In particular, the nice line-reading idiom of the first has to be mangled to fit the readLine call into a try block.
What is the best practice for handling exceptions in a funcion where multiple exceptions could be thrown in its definition?
This one contains all the processing code within the try block:
void processFile(File f)
{
try
{
// construction of FileReader can throw FileNotFoundException
BufferedReader in = new BufferedReader(new FileReader(f));
// call of readLine can throw IOException
String line;
while ((line = in.readLine()) != null)
{
process(line);
}
}
catch (FileNotFoundException ex)
{
handle(ex);
}
catch (IOException ex)
{
handle(ex);
}
}
This one contains only the methods that throw exceptions within try blocks:
void processFile(File f)
{
FileReader reader;
try
{
reader = new FileReader(f);
}
catch (FileNotFoundException ex)
{
handle(ex);
return;
}
BufferedReader in = new BufferedReader(reader);
String line;
while (true)
{
try
{
line = in.readLine();
}
catch (IOException ex)
{
handle(ex);
break;
}
if (line == null)
{
break;
}
process(line);
}
}
The basic premise here is false: the size of a try block makes no difference in performance. Performance is affected by actually raising exceptions at runtime, and that's independent of the size of the try block.
However, keeping try blocks small can lead to better programs.
You might catch exceptions to recover and proceed, or you might catch them simply to report them to the caller (or to a human, via some UI).
In the first case, failures from which you can recover are often very specific, and this leads to smaller try blocks.
In the second case, where an exception is caught so that it can be wrapped by another exception and re-thrown, or displayed to the user, small try blocks mean that you know more precisely which operation failed, and the higher-level context in which that call was made. This allows you to create more specific error reports.
Of course, there are… exceptions (sorry!) to these guidelines. For example, in some cases very specific error reports could be a security problem.
It might be useful to know what effect a try block has on the compiled code. It doesn't change the compiled instructions at all! (Of course, the corresponding catch block does, since it's like any other code.)
A try block creates an entry in the exception table associated with the method. This table has a range of source instructions counters, an exception type, and a destination instruction. When an exception is raised, this table is examined to see if there is an entry with a matching type, and a range that includes the instruction that raised the exception. If it does, execution branches to the corresponding destination number.
The important thing to realize is that this table isn't consulted (and has no effect on running performance) unless it's needed. (Neglecting a little overhead in the loading of the class.)
I've been told that there is some overhead in using the Java try-catch mechanism.
Absolutely. And there's overhead to method calls, too. But you shouldn't put all your code in one method.
Not to toot the premature optimization horn, but the focus should be on ease of reading, organization, etc. Language constructs rarely impact performance as much as system organization and choice of algorithms.
To me, the first is easiest to read.
No. The only thing that you should be considering is where you can reasonably handle the exception and what resources you need to reclaim (with finally).
This is premature optimization at its worst. Don't do it.
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" - Knuth.
there is very very little benefit to the 2nd method. after all if you can successfully open a file but not read from it, then there is something very wrong with your computer. thus knowing that the io exception came from the readLine() method is very rarely useful. also as you know, different exceptions are thrown for different problems anyway (FileNotFoundException, etc)
as long as you scope it with a 'logical' block, ie opening, reading, and closing a file in 1 go, i would go with the first method. it's much simpler to read and, especially when dealing with IO, the processor cycles used by the try-catch overhead would be minimal if any.
Putting the try blocks around the specific code that may throw an exception, makes it, in my opinion easier to read. You're likely to want to display a different message for each error and provide instructions to the user, which will be different depending on where the error occurs.
However, the performance issue that most people refer to is related to raising the exception, not to the try block itself.
In other words, as long as you never have an error raised, the try block won't noticeably affect performance. You shouldn't consider a try block just another flow control construct and raise an error to branch through your code. That's what you want to avoid.
The second method will generate a compiler error that reader may not have been initialized. You can get around that by initializing it to null, but that just means you could get an NPE, and there's no advantage to that.

Forcing FileNotFoundException

I'm writing a test for a piece of code that has an IOException catch in it that I'm trying to cover. The try/catch looks something like this:
try {
oos = new ObjectOutputStream(new FileOutputStream(cacheFileName));
} catch (IOException e) {
LOGGER.error("Bad news!", e);
} finally {
The easiest way seems to make FileOutputStream throw a FileNotFoundException, but perhaps I'm going about this all the wrong way.
Anyone out there have any tips?
You could set cacheFileName to an invalid name or to one you know doesn't exist.
From your comment:
Yes, I suppose the question should
really have been "How do I create a
file that does not exist on both Linux
and Windows?" On windows, I can use
'new File("X:/")', where X: is a drive
letter that does not exist. On Linux,
this does not work because that is a
valid file name.
Look at java.io.File.createTempFile. Use it to create the file and then delete it.
Probably pass it something like:
File tempFile;
tempFile = createTempFile(getClass().getName(),
Long.toString(System.currentTimeMillis());
tempFile.delete();
That should give you a unique name in a platform indendent manner that you can safely use without (much) fear of it existing.
There are two parts to any test: getting it to happen and measuring that you got the correct result.
Fault Injection
The easiest answer is the one that's already been mentioned, which is to set cacheFileName to a file that will never exist. This is likely the most practical answer in this situation.
However, to cause an arbitrary condition such as an IOException, what you really want is Fault Injection. This forces faults in your code without forcing you to instrument your source code. Here are a few methods for doing this:
Mock objects You could use a factory method to create an overridden ObjectOutputStream or FileOutputStream. In test code the implementation would throw an IOException when you wanted to, and in production code would not modify the normal behavior.
Dependency Injection In order to get your Mock Object in the right place you could use a framework such as Spring or Seam to "inject" the appropriate object into the class that's doing the work. You can see that these frameworks even have a priority for objects that will be injected, so that during unit testing you can override the production objects with test objects.
Aspect Oriented Programming Instead of changing the structure of your code at all, you can use AOP to inject the fault in the right place. For instance, using AspectJ you could define a Pointcut where you wanted the exception to be thrown from, and have the Advice throw the desired exception.
There are other answers to fault injection on Java; for instance a product called AProbe pioneered what could be called AOP in C long ago, and they also have a Java product.
Validation
Getting the exception thrown is a good start, but you also have to validate that you got the right result. Assuming that the code sample you have there is correct, you want to validate that you logged that exception. Someone above mentioned using a Mock object for your logger, which is a viable option. You can also use AOP here to catch the call to the logger.
I assume that the logger is log4j; to solve a similar problem, I implemented my own log4j appender which captures log4j output: I specifically capture only ERROR and FATAL, which are likely to be the interesting log messages in such a case. The appender is referenced in log4j.xml and is activated during the test run to capture error log output. This is essentially a mock object, but I didn't have to restructure all my code that got a log4j Logger.
I'm writing a test for a piece of code
that has an IOException catch in it
that I'm trying to cover.
I'm not entirely sure I understand your goal, but if you want to test if the exception is thrown, you can tell the test you expect it to throw the exception:
#Test(expected=IOException.class)
Your test will then fail if the exception is not thrown, and succeed if it is thrown (like, if the cacheFileName file does not exist).
A FileNotFoundException would obviously trigger the catch. The javadoc states the cases where it will be thrown.
You should also consider that the ObjectOutputStream constructor can throw an IOException so may want to cover this case in your tests.
Two easy ways would be either set cacheFileName to a non-existent file or set the specified file to read-only access.
-John
As the code is currently written, you could try to mock out the error() call on the LOGGER object and check to see if it gets called when you expect an IOException.
Your desire to test may have uncovered a fundamental problem with the code as it's written. An error is occurring but there's no boolean or flag value (set the filename to a special pattern) that provides other sections of code to determine if writing to the file was successful. If this is contained in a function, maybe you could return a boolean or set an object level variable.
cacheFileName = "thisFileShouldNeverExistAndIfItDoesYouAreScrewingUpMyTests";
Sure you could take steps and jump thru hoops to programatically make sure that the file name will never, ever, ever exist, or you could use a String that will never exist in 99.99999% of cases.
I hope this is what you meant.
if(new File(cachedFile).exists()) {
oos = new ObjectOutputStream(new FileOutputStream(cacheFileName));
//do your code here
} else {
throw new FileNotFoundException("File doesn't exist!");
}

Categories

Resources