I wanted to test the IOException and IllegalArgumentException thrown by properties.load(in) method. As per the documentation here OracleDoc it says the load method throws IOException - if an error occurred when reading from the input stream. IllegalArgumentException - if the input stream contains a malformed Unicode escape sequence.
Here is my code:
public class PropertiesRetriever {
private String foo;
private String foo1;
private Properties properties;
/**
* Injects the properties file Path in the {GuiceModule}
* Calls {#link PropertiesRetriever#loadPropertiesPath(String) to load the
* properties file.
*/
#Inject
public PropertiesRetriever(#Named("propertiesPath") String propertiesPath, Properties properties)
throws IOException {
this.properties = properties;
loadPropertiesPath(propertiesPath);
}
/**
* Loads the properties file as inputstream.
*
*/
public void loadPropertiesPath(String path) throws IOException {
InputStream in = this.getClass().getResourceAsStream(path);
properties.load(in);
}
Here, a method:
properties.load(in)
throws IOException and IllegalArgumentException. I wanted to test this methods in JUnit testing. Is there anyway I can call these methods.
You can do it by refactoring your code a little. That and use Mockito or some other mocking framework to create an InputStream that behaves as you desire (throw exceptions):
public void loadPropertiesPath(String path) throws IOException {
// Always close streams you open!
try (InputStream in = getIStream(path)) {
properties.load(in);
}
}
private InputStream getIStream(String path) {
InputStream in = this.getClass().getResourceAsStream(path);
return in;
}
You can use mockito to create a partial mock of your object; mock getIStream(String) to return a mock InputStream. Set up the mock to throw the exception you want when InputStream::read(byte[]) gets called.
If you do not want to use PowerMock then you can change the visibility of getIStream(String) to default. Then plain mockito will do the job:
#Test
public void exceptionTest() throws IOException {
PropertiesRetriever pr = new PropertiesRetriever();
PropertiesRetriever prSpy = spy(pr);
InputStream isMock = mock(InputStream.class);
doReturn(isMock).when(prSpy).getIStream(anyString());
doThrow(new IllegalArgumentException("CRASH!")).when(isMock).read(any());
prSpy.loadPropertiesPath("blah");
}
You have two choices. Either provide some test files, that will create expected errors, or pass mock of Stream to Properties retriever as parameter. So instead of propertiesPath parameter, you will have directly inputStream (this approach may just move your problem somewhere else).
If you decide to pass Stream as a parameter, there are some tips, how to mock it: Mocking Java InputStream
Related
I have an AWS Lambda implemented in java. The lambda generates a file, then writes it to the output, using the Base64 encoder. I'm trying to write a unit test for it, but it enters an infinite loop when the file is written.
What I'd like to do is capture what is written to the encodedStream in the unit test, write it to the temporary folder, and then compare the contents to the expected contents, but the test hangs until eventually an out of memory exception is thrown.
Lambda code
public class MyLambda implements RequestStreamHandler {
private static final Logger LOGGER = LogManager.getLogger(MyLambda.class);
#Override
public void handleRequest(#Nonnull InputStream inputStream, #Nonnull OutputStream outputStream, #Nonnull Context context) {
try (OutputStream encodedStream = Base64.getEncoder().wrap(outputStream);){
encodedStream.write("This is written to file".getBytes());
} catch (IOException e) {
LOGGER.info("IOException occurred ", e);
}
}
}
Unit test
public class MyLambdaTest {
#Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
#Test
public void testRequest() throws IOException {
MyLambda myLambda = new MyLambda();
InputStream inputStream = mock(InputStream.class);
OutputStream mockOutputStream = mock(OutputStream.class);
Context mockContext = mock(Context.class);
doNothing().when(mockOutputStream).write(anyInt());
doNothing().when(mockOutputStream).write(any(byte[].class));
doNothing().when(mockOutputStream).write(any(byte[].class), anyInt(), anyInt());
myLambda.handleRequest(inputStream, mockOutputStream, mockContext);
FileUtils.writeByteArrayToFile(temporaryFolder.newFile(), <captured bytes>);
}
}
I have deployed the code to AWS, so I know it works, but I'd like to have a proper unit test written for it for future builds
Instead of mocking the OutputStream, you can create a ByteArrayOutputStream. It's basically just an array of bytes that implements OutputStream. And then you can verify the correct content was written with ByteArrayOutputSteam#toBytes(), or ByteArrayOutputStream#toString()
FileOutputStream fout =
new FileOutputStream(temporaryFolder.newFile("testout.txt"));
MyLambda myLambda = new MyLambda();
myLambda.handleRequest(null, fout, null);
fout.close();
Hi Joseph,
Please find my attempt above. I have used a real FileOutputStream.
How do you mock file reading/writing via JUnit?
Here is my scenario
MyHandler.java
public abstract class MyHandler {
private String path = //..path/to/file/here
public synchronized void writeToFile(String infoText) {
// Some processing
// Writing to File Here
File file = FileUtils.getFile(filepath);
file.createNewFile();
// file can't be written, throw FileWriteException
if (file.canWrite()) {
FileUtils.writeByteArrayToFile(file, infoText.getBytes(Charsets.UTF_8));
} else {
throw new FileWriteException();
}
}
public String readFromFile() {
// Reading from File here
String infoText = "";
File file = new File(path);
// file can't be read, throw FileReadException
if (file.canRead()) {
infoText = FileUtils.readFileToString(file, Charsets.UTF_8);
} else {
throw FileReadException();
}
return infoText
}
}
MyHandlerTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest({
MyHandler.class
})
public class MyHandlerTest {
private static MyHandler handler = null;
// Some Initialization for JUnit (i.e #Before, #BeforeClass, #After, etc)
#Test(expected = FileWriteException.class)
public void writeFileTest() throws Exception {
handler.writeToFile("Test Write!");
}
#Test(expected = FileReadException.class)
public void readFileTest() throws Exception {
handler.readFromFile();
}
}
Given above source, Scenario when file is not writable (write permission not allowed) is OK, However, when i try to do scenario wherein file is not readable (read permission not allowed). It always read the file, i have already tried to modify the file permission on the test code via below
File f = new File("..path/to/file/here");
f.setReadable(false);
However, I did some reading, setReadable() always returns false (failed) when run on Windows machine.
Is there a way to modify the file permission of the target file programmatically in relation to JUnit?
Note
Target source code to test cannot be modified, meaning
Myhandler.class is a legacy code which is not to be modified.
Instead of relying on the operating system file permissions, use PowerMock to mock FileUtils.getFile(...) and make it return an instance of File (e.g. anonymous sub class) that returns a specific value for canWrite()/canRead().
Mocking static methods with Mockito
Since Mockito cannot mock static methods, use a File factory instead (or refactor your FileUtils to be a factory), then you can mock it and return a mocked File instance as well, where you can also mock any File methods you want.
So instead of FileUtils.getFile(filepath) you will now have something like FileFactory.getInstance().getFile(filepath) for example, where you can mock getFile(String) method easily.
In jUnit there's a handy rule for scenarios like yours.
public class MyHandlerTest {
#Rule
// creates a temp folder that will be removed after each test
public org.junit.rules.TemporaryFolder folder = new org.junit.rules.TemporaryFolder();
private MyHandler handler;
#Before
public void setUp() throws Exception {
File file = folder.newFile("myFile.txt");
// do whatever you need with it - fill with test content and so on.
handler = new MyHandler(file.getAbsolutePath()); // use the real thing
}
// Test whatever behaviour you need with a real file and predefined dataset.
}
This is my utility class to mock the service
public class MockService {
public static void bootUpMockServices() throws IOException {
String orderServiceSpecification = readFile("mappings/orderServicesSpecifications.json", Charset.defaultCharset());
String singleOrder = readFile("mappings/singleOrder.json", Charset.defaultCharset());
WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/orders"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withBody(orderServiceSpecification)));
WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/orders/1"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withBody(singleOrder)));
}
public static String readFile(String path, Charset encoding)
throws IOException {
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
}
As you can see I'm mocking a GET call /orders (with all the orders) and responding with the body with all the orders kept in a json file.
I'm also calling a single order by GET call /orders/1. I'm responding it with an JSON object in a file. But I want it to be dynamic. Like when I hit it with orders/30 then, I should be dynamically fetch order with id=30 and render it.
Currently, if you want dynamic behaviour of the kind you described you'll need to write a ResponseDefinitionTransformer and register it with the WireMockServer or WireMockRule on construction.
This is documented here: http://wiremock.org/docs/extending-wiremock/#transforming-responses
Example of a transformer implementation here:
https://github.com/tomakehurst/wiremock/blob/master/src/test/java/com/github/tomakehurst/wiremock/ResponseDefinitionTransformerAcceptanceTest.java#L208-L222
What you're trying to do could be done pretty straightforwardly with a stub mapping matching on a URL regex something like /orders/(\d+) and a transformer that parses out the number part then modifies the bodyFileName on the ResponseDefinition.
I want to test StreamDecorator class:
public interface IDecorator {
default InputStream decorateStream() {
if("gzip".equalsIgnoreCase(getEncoding())) {
return new GZIPInputStream(getInputStream());
}
return is;
}
String getEncoding();
InputStream getInputStream();
}
public class StreamDecorator implements IDecorator {
private final InputStream is;
private final String encoding;
public StreamDecorator(InputStream is, String encoding) {
this.is = is;
this.encoding = encoding;
}
public String getEncoding() {
return encoding;
}
public InputStream getInputStream() {
return is;
}
}
To test it, I have written the following test case:
#RunWith(PowerMockRunner.class)
public class TestStreamDecorator {
#Test
#PrepareForTest(value = { GZIPInputStream.class })
public void testDecorateStream() throws Exception {
InputStream mockInputStream = PowerMock.createMock(InputStream.class);
EasyMock.expect(mockInputStream.read()).andReturn(-1);
PowerMock.replay(mockInputStream);
GZIPInputStream gzip = PowerMock.createMock(GZIPInputStream.class);
PowerMock.expectNew(GZIPInputStream.class, mockInputStream).andReturn(gzip);
PowerMock.expectNew(GZIPInputStream.class, mockInputStream, 512).andReturn(gzip);
PowerMock.replay(gzip);
PowerMock.replay(GZIPInputStream.class);
StreamDecorator inStreamDecorator = new StreamDecorator(mockInputStream, "gzip");
assertEquals(gzip, inStreamDecorator.decorateStream());
PowerMock.verify(mockInputStream, gzip, GZIPInputStream.class);
}
}
When I run the unit test, I get java.io.EOFException (see below).
I know the reason for this exception -- it is because in second statement in TestStreamDecorator.testDecorateStream() method I'm returning -1 for InputStream.read().
But, my question is why is it even getting called when I have mocked the constructors (i.e. new instances)?
Also, I have tried mocking private readHeader() method of GZIPInputStream -- but getting different exception in another private method.
So, I guess, I'm doing something wrong.
===================== Exception =====================
java.io.EOFException
at java.util.zip.GZIPInputStream.readUByte(GZIPInputStream.java:268)
at java.util.zip.GZIPInputStream.readUShort(GZIPInputStream.java:258)
at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:164)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:91)
...
My guess is PowerMock has some issues with Java 8 default methods. Anything in that direction?
You are actually verifying the mock in your last line of test case which is why you are getting the exception
Delete last two lines of your test and add this and you test case passes
assertEquals(mockInputStream, inStreamDecorator.decorateStream());
I have the following class:
public class FileLoader {
private Map<Brand, String> termsOfUseText = new HashMap<Brand, String>();
public void load() {
for (Brand brand : Brand.values()) {
readAndStoreTermsOfUseForBrand(brand);
}
}
private void readAndStoreTermsOfUseForBrand(Brand brand) {
String resourceName = "termsOfUse/" + brand.name().toLowerCase() + ".txt";
InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
try {
String content = IOUtils.toString(in);
termsOfUseText.put(brand, content);
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName),e);
}
}
public String getTextForBrand(Brand brand) {
return termsOfUseText.get(brand);
}
}
Brand is an enum, and I need all the valid .txt files to be on the classpath. How do I make the IOException occur, given that the Brand enum contains all the valid brands and therfore all the .txt files for them exist?
Suggestions around refactoring the current code are welcome if it makes it more testable!
Three options I see right off:
Use PowerMock to mock IOUtils.toString(). I consider PowerMock to be quite a last resort. I'd rather refactor the source to something a little more test-friendly.
Extract the IOUtils call to a protected method. Create a test-specific subclass of your class that overrides this method and throws the IOException.
Extract the InputStream creation to a protected method. Create a test-specific subclass to override the method and return a mock InputStream.
I would suggest a bit of refactoring. All your methods are void, this usually means they are not functional.
For example, you can extract this functionality:
private String readTermsOfUseForBrand(InputStream termsOfUserIs) {
try {
String content = IOUtils.toString(in);
return content;
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName), e);
}
return null;
}
So that we can assert on the String result in our tests.
Of course this is not functional code, as it reads from an Input Stream. And it does so with IOUtils.toString() method that cannot be mocked easily (well, there's PowerMock but as Ryan Stewart said it's the last resort).
To test IO exceptions you can create a failing input stream (tested with JDK7):
public class FailingInputStream extends InputStream {
#Override
public int read() throws IOException {
throw new IOException("Test generated exception");
}
}
And test like that:
#Test
public void testReadTermsOfUseForBrand() {
FileLoader instance = new FileLoader();
String result = instance.readTermsOfUseForBrand(new FailingInputStream());
assertNull(result);
}
Missing file will cause NullPointerException because getResourceAsStream will return null and you will have in==null. IOException in this case may actually be pretty rare. If it's critical for you to see it, I can only think of instrumenting this code to throw it if code is executed in test scope. But is it really that important?
I would use a mock to accomplish this.
Example (untested, just to give you some thought):
#Test(expected=IllegalStateException.class)
public void testThrowIOException() {
PowerMockito.mockStatic(IOUtils.class);
PowerMockito.when(IOUtils.toString()).thenThrow(
new IOException("fake IOException"));
FileLoader fileLoader = new FileLoader();
Whitebox.invokeMethod(fileLoader,
"readAndStoreTermsOfUseForBrand", new Brand(...));
// If IllegalStateException is not thrown then this test case fails (see "expected" above)
}
Code below is completely untested
To cause the IOException use:
FileInputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
in.mark(0);
//read some data
in.reset(); //IOException
To test the IOException case use:
void test
{
boolean success = false;
try
{
//code to force ioException
}
catch(IOException ioex)
{
success = true;
}
assertTrue(success);
}
In JUnit4
#Test(expected=IOException.class)
void test
{
//code to force ioException
}
Other JUnit
void test
{
try
{
//code to force IOException
fail("If this gets hit IO did not occur, fail test");
}
catch(IOException ioex)
{
//success!
}
}