views:

94

answers:

4

I have a method which tries to call an in-memory image converter, and if that fails, then tries to do the image conversion on disk. (The in-memory image converter will try to allocate a second copy of the image, so if the original is very large, we might not have sufficient memory for it.)

public BufferedImage convert(BufferedImage img, int type) {
  try {
    return memory_converter.convert(type);
  }
  catch (OutOfMemoryError e) {
    // This is ok, we just don't have enough free heap for the conversion.
  }

  // Try converting on disk instead.
  return file_converter.convert(img, type);
}

I'd like to write unit tests for JUnit which exercise each code path, but it's inconvenient to run JUnit with little enough heap to force an OutOfMemoryError. Is there some way to simulate an OutOfMemoryError within JUnit?

It has occurred to me that I could make a fake subclass of BufferedImage that throws an OutOfMemoryError the first time a method called by the in-memory converter is called, but then behaves normally on subsequent calls. This seems like a hack, though.

+3  A: 

Inject a stub or mock memory_converter whose convert() method throws OutOfMemryError.

It has occurred to me that I could make fake subclass of BufferedImage that throws an OutOfMemoryError the first time a method called by the in-memory converter is called, but then behaves normally on subsequent calls. This seems like a hack, though.

Mocking frameworks are generally very powerful and should let you specify this behavior. For example, JMock:

http://www.jmock.org/returning.html

Returning Different Values on Consecutive Calls

There are two ways to return different values on different calls. The first is to define multiple expectations and return a different value from each:

oneOf (anObject).doSomething(); will(returnValue(10));
oneOf (anObject).doSomething(); will(returnValue(20));
oneOf (anObject).doSomething(); will(returnValue(30));

The first invocation of doSomething will return 10, the second 20 and the third 30.

Bert F
+6  A: 

You should be mocking out your converter, rather than using a real one.

Once you do that, you simply have your mocking library throw a new OOME when the convert() method is called.

For example, with JMock you would do this:

allowing(mockConverter).convert(with(any(int.class)));
will(throwException(new OutOfMemoryError()));
dty
+1  A: 

It has occurred to me that I could make fake subclass of BufferedImage that throws an OutOfMemoryError the first time a method called by the in-memory converter is called, but then behaves normally on subsequent calls. This seems like a hack, though.

You are headed down the right path here. Mocking out objects is exactly what these types of things are beneficial for. You don't really care if the OutOfMemory error is legit, you just want to make sure it is thrown/caught and the other path is executed. Mock it out and you will be good to go.

Aaron
A: 

The handling of an OutOfMemoryError and the testing of it is very difficult. You can not test it with a mock. Depending on which place the OutOfMemoryError exception occur the effect can be very different. The problem is that the OutOfMemoryError can not be pass through to your calling code.

If you want a real test then you need to allocate in a second thread memory and produce a an OutOfMemoryError. This should you repeat multiple time to see different effects.

If you want only test your catch block then you can mock it.

Because a OutOfMemoryError can produce a fatal error on any other thread in your application you should prevent it. That I think a better solution is to calculate the free memory. The needed memory size and call the on disk conversion before the exception occur.

Horcrux7
The problem I see with checking the amount of free heap is that finding that it's too little is no guarantee that that the allocation will fail---attempting the allocation could cause the garbage collector to free enough heap for it to succeed.
uckelman