Use mockito and junit to test images - java

I have a method that gets as parameter a MultipartFile object. Inside the method I use ImageIO.read(some_value) and ImageIO.write(some_value). I want to test this method with a mock image (I don't want to have images stored under the resource folder).
I've tried this:
MockMultipartFile file = new MockMultipartFile("file", "boat.jpg", "image/jpeg", "content image".getBytes()); but without success.
public void f(MultipartFile file) throws IOException {
final BufferedImage read = ImageIO.read(new ByteArrayInputStream(file.getBytes()));
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(read, "jpg", baos);
}
}
When I run the test the read variable has null value. I think that problem come from "content image".getBytes().
Is there a posibility to use mock images instead of real ones ?

"content image".getBytes() returns a byte[] representation of the String "content image". How is ImageIO supposed to construct a BufferedImage from that?
You have two options here.
Pass a byte[] of real data to MockMultipartFile
Since you mentioned you don't want to use mock image resources, this does not seem like a good fit.
Mock ImageIO's static methods using Powermock
The mocked method will return a real BufferedImage your method can use, without having to read an image from a file.
This gives you the added benefit of being able to mock the call to write() as well, if you wish.
Sample code:
PowerMockito.mockStatic(ImageIO.class);
when(ImageIO.read(any())).thenAnswer(invocation -> {
Object argument = invocation.getArguments()[0];
// here you can check what arguments you were passed
BufferedImage result = new BufferedImage(600, 400, BufferedImage.TYPE_INT_RGB); // create a BufferedImage object
// here you can fill in some data so the image isn't blank
return result;
});
Now, when your method under test calls imageIO.read(), it will receive the BufferedImage you construct in the lambda, without actually reading any files.

Related

Junit - mock a file

I'm trying to cover code that process a file. I'm trying to avoid using real file for tests, so I'm using Mockito.
This is the code I'm trying to test:
try {
byte[] data = Files.readAllBytes(((File) body).toPath());
immutableBody = data;
actualHeaderParams.put(HttpHeaders.CONTENT_LENGTH, (new Integer(data.length)).toString());
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
I'm using mock file:
File mockedFile = Mockito.mock(File.class);
but I get an Exception on 'toPath'. So I added some path or null, but then again I get Exceptions since the file doesn't exist in the path.
when(mockedFile.toPath()).thenReturn(Paths.get("test.txt"));
getting:
com.http.ApiException: There was a problem reading the file: test.txt
Is there any way doing it without creating a real file for the test?
Since you want to mock reading of files I assume you have some logic in this class which you would like to test in isolation (without using actual files), therefore I suggest to:
Move the responsibility of reading files into a separate class, so instead of having:
byte[] data = Files.readAllBytes(((File) body).toPath());
interleaved with your business logic, have:
byte[] data = fileReader.read(body);
and fileReader will be an instance of your class with a very simple implementation along these lines:
class FileToBytesReader {
byte[] read(File file) throws IOException {
return Files.readAllBytes(((File) body).toPath());
}
}
then in your test you can subsitute fileReader with a mock on which you can set expectations.
If you are using Java 8 you do not have to create the FileToBytesReader class, but you can use java.util.Function:
Function<File, byte[]> fileReader = (file) -> {
try {
return Files.readAllBytes(((File) file).toPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
BTW. If you are working on a legacy code and you cannot change the production code, then you have to use PowerMock to mock this static method.
I'm not sure there is an easy way but I might be wrong. You would probably need to mock the static Files.readAllBytes() method which you would need to use something like PowerMock to do. Or you could wrap this in a method which you can then mock the behaviour of:
public byte[] getAllBytesWrapper(File body) {
return Files.readAllBytes(body.toPath());
}
and then have a mock for this method:
when(classUnderTest.getAllBytesWrapper(any(File.class))).thenReturn("test".getBytes());
Mock Files.readAllBytes() with Matchers.any() as arguments. and return a byte array.

GSON can't serialize BufferedImages

There seems to be a problem with serializing BufferedImages in JSON using GSON. I am using Derby to store images. When I query the the database I build a JavaBean that has some text fields and one BufferedImage field. I then use GSON to convert the JavaBean into JSON, and this is where the exception occurs.
The Exception message is below:
java.lang.IllegalArgumentException: class sun.awt.image.ByteInterleavedRaster declares multiple JSON fields named maxX
I did find similar problems here GSON java.lang.IllegalArgumentException: class 'xx' declares multiple JSON fields named 'XX' AND StackOverflowError and here class A declares multiple JSON fields
But the problem is with the awt library included with Java. I could follow the answers provided in those other stackoverflow answers if I could access the AWT source code, but how do I do that?
You have to know that not every class is designed to be (de)serialized, especially if (de)serialization is based on the target class binary structure. Your approach has at least the following weak points:
the sun.awt.image.ByteInterleavedRaster class fields are not necessarily the same on another JVM/JRE, thus you can get be vendor-locked;
persisting binary data in JSON is probably not the best choice (probably huge and terrible memory consumption during (de)serialization, storage consumption, performance) -- maybe a generic blob storage is better for binary data?
reading an images with Java AWT and writing it back does not guarantee the same binary output: for example, my test image, 1.2K, was deserialized as an image of another size, 0.9K;
you must choose the target persisting image format or detect the most efficient one (how?).
Consider the following simple class:
final class ImageHolder {
final RenderedImage image;
ImageHolder(final RenderedImage image) {
this.image = image;
}
}
Now you have to create a type adapter to tell Gson how a particular type instance can be stored and restored:
final class RenderedImageTypeAdapter
extends TypeAdapter<RenderedImage> {
private static final TypeAdapter<RenderedImage> renderedImageTypeAdapter = new RenderedImageTypeAdapter().nullSafe();
private RenderedImageTypeAdapter() {
}
static TypeAdapter<RenderedImage> getRenderedImageTypeAdapter() {
return renderedImageTypeAdapter;
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final RenderedImage image)
throws IOException {
// Intermediate buffer
final ByteArrayOutputStream output = new ByteArrayOutputStream();
// By the way, how to pick up the target image format? BMP takes more space, PNG takes more time, JPEG is lossy...
ImageIO.write(image, "PNG", output);
// Not sure about this, but converting to base64 is more JSON-friendly
final Base64.Encoder encoder = Base64.getEncoder();
// toByteArray() returns a copy, not the original array (x2 more memory)
// + creating a string requires more memory to create the String internal buffer (x3 more memory)
final String imageBase64 = encoder.encodeToString(output.toByteArray());
out.value(imageBase64);
}
#Override
public RenderedImage read(final JsonReader in)
throws IOException {
// The same in reverse order
final String imageBase64 = in.nextString();
final Base64.Decoder decoder = Base64.getDecoder();
final byte[] input = decoder.decode(imageBase64);
return ImageIO.read(new ByteArrayInputStream(input));
}
}
Note that Gson is currently NOT very well designed to support byte transformation, however it might be somewhat better in the future if fixed.
Example use:
private static final Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(RenderedImage.class, getRenderedImageTypeAdapter())
.create();
public static void main(final String... args)
throws IOException {
try ( final InputStream inputStream = getPackageResourceInputStream(Q43301580.class, "sample.png") ) {
final RenderedImage image = ImageIO.read(inputStream);
final ImageHolder before = new ImageHolder(image);
final String json = gson.toJson(before);
System.out.println(json);
final ImageHolder after = gson.fromJson(json, ImageHolder.class);
...
}
}
Example output (with real tiny (32x32) PNG file inside):
{"image":"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADgklEQVR42t2XXUiTYRTHpxj4kSKShhgYGSihZGIXXYhU5J2BhBIhCH5cCF6oiWhG0k1BpHghgRgoJHiloBKEqFQ3frDNuemaOqdu0+n8mFM3Nzf37z1n+JZUEPlOoQdetvd5L87vOed/Ph4ZznnJzsqQz+uFz+M5HwBrezuUFy9CERoKY3U1jtzuwAFY29pgGxgQ350aDVSXLmFfLud9eVAQTHV1gQNYKi+HMiwM9uFhft/o6MBcTg6fWp+XB93duzhyOOA7POSwyAIR64UnTxhi9+tXfhQhIdBlZ2P2wQM2Tmv11StY3rwJjAYIQl9QAGVUFPZGRzF7/z7kwcGw9ffzt80PHzAZE4ODuTnpAQ50OjgmJ3HkcmE+N5chdr98wfzDh5DLZPyo4uOx+/mz9Bqg+B8b0d6+zSecFeJPInSo1XAbjXAKvxR/yUW4Pz7uV/vEBJ9OffUqNNev49BiYeGp4uLg0usDUwdIUNNpaTDV1op7rqUljvNKYyMLb7G4GIdWa2AAbH19LDIy8vNaefmSBRiQUkynMtXUYLGkBO7lZWx2dTEEnVjURFnZL1CSASyWlmL6xg1okpIwdeUK3CYTNjo7WYCGoiLOeU1yMtxmc2AA1NeuscA829uYTk1lEIJYf/eOIcgzP6tdEgAyRicjtatiY8V9EhdDpKTw/7XmZoYgGEkBzEITIQDzs2dsYPX1a/EbuZq8YG5o8GeG8E2dmIgjp/P0AJxGgku1GRnYVyh479jVdFrRE+vrXGqPl3dvTxoPeO12aDMz2aBDqRT315qa/trV/wTgsdmw1d3NJVSMs+BmOqlYhARXL1dUSA/gWljg9FKGh/u72tgYQ1BqEcjvqtqpAHY+fcLOx4/+durzcTOxvH3LXY1qOUFQ/CnVyAszN2+eGK1OBWCur4cyIgIrL174Xb+1hdl79xiERioqOFRSKf3sQ0MclvXWVmk8sN3b6+9UBsMvQwWtb3fuwD4ywpkwlZDAojNWVUk3lhsrK7Hw+PHJ+AudzKnVwrOzwwYP5ud50JhJT5cs9iLAxvv3UFy4wLVdn58P1eXLP4YKIfWor09GR0MZGYm1lhbpLyYUZ/Pz55i5dQu6rCwYnz4FhYXmNjJKKbYmiHG7p+fsb0aGwkIsC2PWuVzNaJ5j1Q8Oni0AVTkKCbmffs/8cuoVlK9/9IjHrP/qdvyn9R0SEM4flWsmCwAAAABJRU5ErkJggg\u003d\u003d"}
I think there are too many flaws, and I would strongly recommend you to redesign your binaries storage if possible and store binary content as-is.

Getting print calls in LuaJ

I'm writing a Java program which uses Lua scripts to determine what to output to certain areas of the program. Currently, my code looks as such:
Globals globals = JsePlatform.standardGlobals();
LuaValue chunk = globals.loadfile(dir.getAbsolutePath() + "/" + name);
chunk.call();
String output = chunk.tojstring();
The problem is that calling tojstring() appears to return return values from the Lua script. This is fine, but I need to get print calls, as that's what will be displayed on the screen. As of now, the print calls get sent directly to the Console (printed to console), and I cannot figure out a way to retrieve these print calls.
I've tried digging through the documentation but have had little success. Will change from LuaJ if needed.
Expanding on Joseph Boyle's answer (a few years later): You can also set up a printStream to a ByteArrayOutputStream (no need to do it to a file on disk) if that's your poison. I did this in a JUnit test with LuaJ and it works:
#Test
public void testPrintToStringFromLuaj() throws IOException {
String PRINT_HELLO = "print (\"hello world\")";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(baos, true, "utf-8");
Globals globals = JsePlatform.standardGlobals();
globals.STDOUT = printStream;
LuaValue load = globals.load(PRINT_HELLO);
load.call();
String content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
printStream.close();
assertThat(content, is("hello world\n"));
}
I actually was able to solve the problem by changing the STDOUT variable in the globals object to a temporary file, and then reading the data from the temporary file.
Probably not the best solution, but works perfectly fine.

How to convert byte array to buffered image

I have a server-side java code that gets a byte array from the client. In order to do some image processing, I need to convert the byte array into a BufferedImage. I have a code that's supposed to do that here:
public void processImage(byte[] data) {
ByteArrayInputStream stream = new ByteArrayInputStream(data);
BufferedImage bufferedImage;
bufferedImage = ImageIO.read(stream);
// bufferedImage is null
//...
}
But this doesn't work; bufferedImage is null. According to the ImageIO documentation:
If no registered ImageReader claims to be able to read the resulting stream, null is returned.
How do I tell the ImageReader what image type it is. For instance, if I know the image to be JPEG (which it is, in my case), what am I supposed to do?
EDIT: Thanks for the suggestion that the file is most likely not in JPEG format. This is the client-side code I have that sends the data as String over to the server:
import org.json.JSONObject;
// Client-side code that sends image to server as String
public void sendImage() {
FileInputStream inputStream = new FileInputStream(new File("myImage.jpg"));
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] b = new byte[1024];
while ((bytesRead = inputStream.read(b)) != -1) {
byteStream.write(b,0,bytesRead);
}
byte[] byteArray = byteStream.toByteArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("data",new String(byteArray));
// ... more code here that sends jsonObject in HTTP post body
}
And this is the server-side code that calls the processImage() function:
// Server-side code that calls processImage() function
public void handleRequest(String jsonData) {
JSONObject jsonObject = new JSONObject(jsonData);
processImage(jsonObject.getString("data").getBytes());
}
The most likely explanation is that the byte array doesn't contain a JPEG image. (For instance, if you've just attempted to download it, you may have an HTML document giving an error diagnostic.) If that's the case, you'll need to find what is causing this and fix it.
However, if you "know" that the byte array contains an image with a given format, you could do something like this:
Use ImageIO.getImageReadersByFormatName or ImageIO.getImageReadersByMIMEType to get an Iterator<ImageReader>.
Pull the first ImageReader from the Iterator.
Create an MemoryCacheImageInputStream wrapping a ByteArrayInputStream for the types.
Use ImageReader.setInput to connect the reader to the ImageInputStream.
Use ImageReader.read to get the BufferedImage.

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: im == null

hi i'm working on a project in which i need to make changes to BASE64 string of an image(jpg)...so at first when i didn't made any changes, the ImageReader was properly working and my image was displayed properly..but when i made changes to my BASE64 string the above exception came..i searched a lot and came to know that im==null comes when ByteStream is not jpeg,png,gif..etc..so what if i have a new type of ByteStream...what should i use?? or what ever my BASE64 string is i need to convert that to an image..so how can i do that??
here is my code snippet:this is to convert BASE64 string to an image
public static BufferedImage decodeToImage(String imageString) throws IOException {
BufferedImage image = null;
byte[] imageByte;
try {
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(imageString);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
}
catch (Exception e) {
e.printStackTrace();
}
ImageIO.write(image, "jpg", new File("d:/CopyOfTestImage.jpg"));
return image;
}
Have a look at the Javadocs for ImageIO.read:
Returns a BufferedImage as the result of decoding a supplied InputStream with an ImageReader chosen automatically from among those currently registered. The InputStream is wrapped in an ImageInputStream. If no registered ImageReader claims to be able to read the resulting stream, null is returned. [emphasis mine]
The read method can return null, yet you are not checking for this. In fact the method is probably returning null, which is why ImageIO.write throws an exception when you pass null into it.
First things first, you need to check for error conditions and handle them appropriately (including the null return, but also including any exceptions that are thrown, which you currently catch and ignore).
Now if you're getting null back from ImageIO.read, it means the bytes you passed into the read method did not appear to be a valid image in any known format. You need to look in more detail at the modifications you're making to the base64 string, and ensure that what you're doing is valid, and results in a valid image. Alternatively, if you're getting some other exception thrown, then you need to handle that appropriately.
(As a general rule, don't throw away/skip over errors, because then when things go wrong you have no idea why!)
change this line:
ImageIO.write(image, "jpg", new File("d:/CopyOfTestImage.jpg"));
to something like this
image = ImageIO.read(getClass().getResource("/resources/CopyOfTestImage.jpg"));

Categories