I have two processes - FileWriter and FileReader.
FileWriter has a write() method which creates the file if it's missing and writes to the file. FileReader has a read() method which reads from the file and throws an CustomException if the file is not available.
During the normal application run, FileWriter.write() method is executed first followed by FileReader.read() method. Since the file will be always present, read() method never throws my CustomException unless something went wrong with FileWriter.
I am writing junit testcases for both these classes. When testing both classes independent of each other, I found my custom exception is being thrown when file is not present. For a really convoluted reason, I want the testcase to be marked as success and execute the next test. To achieve this, I did the below:
#Test
public void testRead() throws CustomException {
boolean assumeTestcasePassed = false;
FileReader fileReader = new FileReader();
String fileContent = null;
try {
fileContent = fileReader.read();
} catch (CustomException e) {
assumeTestcasePassed = true;
}
if(assumeTestcasePassed){
assertTrue(true);
} else {
assertTrue("File is empty", fileContent != null);
}
}
Is there a better way to achieve what I am doing here?
If I understand correctly, you are expecting an exception to be thrown, just do this:
#Test
public void testRead() {
try {
assertTrue(new FileReader().read() != null);
} catch (CustomException e) {
// Test passes
}
}
You can also annotate like this answer: How do you assert that a certain exception is thrown in JUnit 4 tests?
You can expect the tested code to throw an exception in the #Test annotation:
#Test(expected = CustomException.class)
public void testRead() throws CustomException {
FileReader fileReader = new FileReader();
fileContent = fileReader.read();
}
Related
I have the following code that reads data from a csv file that I am trying to write a unit test for. I am unsure of how to go about it.
public class BudgetTags implements BudgetTagsList{
// State variables
private Set<String> tags = new TreeSet<>();
private String tag_file_path;
public BudgetTags(String tag_file_path){
//Retrieve tags from tag file
this.tag_file_path = tag_file_path;
this.retrieveTags();
}
public void retrieveTags() {
String line = "";
try{
// Begin reading each line
BufferedReader br = new BufferedReader(new FileReader(this.tag_file_path ));
while((line = br.readLine()) != null){
String[] row = line.split(",");
this.tags.add(row[0]); //Assume correct file format
}
br.close();
} catch (IOException e){
System.out.println("Fatal exception: "+ e.getMessage());
}
}
}
Note that the method retrieveTags(); is not allowing me to specify an additional FileNotFoundException since it extends IOException. It is being tested in the following manner:
#Test
#DisplayName("File name doesn't exist")
void testRetrieveTag3() {
String path = "test\\no_file.csv";
//Instantiate new tags object
BudgetTags tags = new BudgetTags(path);
IOException thrown = assertThrows(IOException.class, () -> tags.retrieveTags());
}
The variable path does not exist so I am expecting the test to catch the IOException, (although I would prefer a FileNotFoundException) . When I run this particular test, I receive an AssertionFailedError How can I restructure my test so that it catches the FileNotFoundException when a new tags object is instantiated, since retrieveTags() is called when a new tags object is generated?
The method retrieveTags() will not allow me to specify
The method is not actually throwing the exception but catching it. What you actually need to test is that your catch block gets executed. If all you want to do on catching the exception is printing the error, test system.out can help you assert the print statement
Your assertThrows test is failing becuase it's impossible for the constructor to throw an IOException. For one, it's a checked exception, which means both the constructor and the method would require a throws IOException clause. Second, you catch the exception; it's not thrown out of the method.
Based on your test, it should look more like this:
public class BudgetTags implements BudgetTagsList {
private final Set<String> tags = new TreeSet<>();
private String tagFilePath;
public BudgetTags(String tagFilePath) throws IOException {
this.tagFilePath = tagFilePath;
retrieveTags(); // can throw IOException
}
public void retrieveTags() throws IOException {
// note: use try-with-resources to handle closing the reader
try (BufferedReader br = new BufferedReader(new FileReader(tagFilePath))) {
String line;
while ((line = br.readLine()) != null) {
String row = line.split(",");
tags.add(row[0]);
}
}
// don't catch the exception; your test indicates you want it
// thrown out to the caller
}
}
class BudgetTagsTests {
#Test
#DisplayName("File does not exist")
void testRetrieveTags3() {
String tagFilePath = "test/no_file.csv";
// note: we need to test on the constructor call, because you call
// 'retrieveTags()' in it.
assertThrows(FileNotFoundException.class, () -> new BudgetTags(tagFilePath));
}
}
By passing FileNotFoundException.class, the test will fail if any other IOException is thrown.
You should not be catching the IOException the way you are, anyway. Yes, you log it, which means if you look at the logs you'll be aware that something went wrong. But other code won't know something went wrong. To that code, it will appear as if there were simply no tags in the file. By throwing the IOException out to the caller of retrieveTags(), you're letting the caller react to the exception as needed. And if the call succeeds, but the tags are empty, then it knows the file exists but simply had no tags.
Also, you say:
Note that the method retrieveTags(); is not allowing me to specify an additional FileNotFoundException since it extends IOException.
I'm not sure what exactly you tried from that statement, but it is possible to catch more specific exceptions even though you're also catching the more general exception. It's just that the order of the catch blocks matter:
try {
somethingThatThrowsIOException();
} catch (FileNotFoundException ex) {
// do something special for when the file doesn't exist
} catch (IOException ex) {
// handle general exception
}
The more specific exception must be caught before the more general exception.
How can I test assertion immediately following an exception with EasyMock?
For example, there is a method storeIntoFile() which retrieves an object and writes it into a file. In case of an exception, this file is deleted. I'm looking to test this method specifically to verify that the file gets deleted on encountering an exception.
I have the following test to do this:
#Test (expected IOException.class)
public void testConnectionFailure throws IOException {
File storeFile = File.createTempFile(
"test",
"test"
);
storeIntoFile(storeFile);
Assert.assertFalse(storeFile.exists());
}
However in this case, the test completes as soon as the exception is encountered during the storeIntoFile call and does not proceed to test the following assertion. How can I test this assertion after the exception without using mock objects?
It's more a JUnit question than EasyMock. With JUnit 4.13, you can do the following.
public class MyTest {
public interface FileRepository {
void store(File file) throws IOException;
}
private void storeIntoFile(File file) throws IOException {
try {
repository.store(file);
} catch(IOException e) {
file.delete();
throw e;
}
}
private final FileRepository repository = mock(FileRepository.class);
#Test
public void testConnectionFailure() throws IOException {
File storeFile = File.createTempFile("test", "test");
IOException expected = new IOException("the exception");
repository.store(storeFile);
expectLastCall().andThrow(expected);
replay(repository);
IOException actual = assertThrows(IOException.class, () -> storeIntoFile(storeFile));
assertSame(expected, actual);
assertFalse(storeFile.exists());
}
}
I do not recommend the expected exceptions. assertThrows is much better since it allows to assert on the exception.
I am trying to test file manipulation with my APP. First of all I wanna check that whenever I call a function that reads the file, this function will throw an Exception because the file isn't there.
However, I don't seem to understand how to achieve this... This is the code I designed, but it doesn't run ... the normal JUNIT says the FILEPATH wasn't found, the android JUNIT says, the Test could not be run.
The folder: /data/data/example.triage/files/ is already available in the virtual device...
#Before
public void setUp() throws Exception {
dr = new DataReader();
dw = new DataWriter();
DefaultValues.file_path_folder = "/data/data/example.triage/files/";
}
#After
public void tearDown() throws Exception {
dr = null;
dw = null;
// Remove the patients file we may create in a test.
dr.removeFile(DefaultValues.patients_file_path);
}
#Test
public void readHealthCardsNonExistentPatientsFile() {
try {
List<String> healthcards = dr.getHealthCardsofPatients();
fail("The method didn't generate an Exception when the file wasn't found.");
} catch (Exception e) {
assertTrue(e.getClass().equals(FileNotFoundException.class));
}
}
It doesn't look like you are checking for the exception in a way that correlates with the JUnit API.
Have you tried to make the call:
#Test (expected = Exception.class)
public void tearDown() {
// code that throws an exception
}
I don't think you want the setup() function to be able to generate an exception, since it is called before all other test cases.
Here's another way to test exceptions:
Exception occurred = null;
try
{
// Some action that is intended to produce an exception
}
catch (Exception exception)
{
occurred = exception;
}
assertNotNull(occurred);
assertTrue(occurred instanceof /* desired exception type */);
assertEquals(/* expected message */, occurred.getMessage());
So I would make you setup() code not throw an exception and move the exception generating code to a test method, using an appropriate way to test for it.
I'm trying to learn the ins and outs of various mocking libraries and PowerMock(specifically the EasyMock extension) is next on the list. I'm attempting to mock a constructor and the examples provided don't have the same response when I try to replicate them. As far as I can tell, it never mocks the constructor and just proceeds as if it were normal.
This is the test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Writer.class})
public class FaultInjectionSituationTest {
#Test
public void testActionFail() throws Exception {
FaultInjectionSituation fis = new FaultInjectionSituation();
PowerMock.expectNew(Writer.class, "test")
.andThrow(new IOException("thrown from mock"));
PowerMock.replay(Writer.class);
System.out.println(fis.action());
PowerMock.verify(Writer.class);
}
}
I've tried replacing the "test" with an EasyMock.isA(String.class), but it yielded the same results.
This is the FaultInjectionSituation:
public class FaultInjectionSituation {
public String action(){
Writer w;
try {
w = new Writer("test");
} catch (IOException e) {
System.out.println("thrown: " + e.getMessage());
return e.getLocalizedMessage();
}
return "returned without throw";
}
}
The "Writer" is nothing more than a shell of a class:
public class Writer {
public Writer(String s) throws IOException {
}
public Writer() throws IOException{
}
}
When the test is run, it prints out "returned without throw", indicating the exception was never thrown.
You need to prepare the class that is calling the constructor as well, so PowerMock knows to expect a mocked constructor call. Try updating your code with the following:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Writer.class, FaultInjectionSituation.class})
public class FaultInjectionSituationTest {
// as before
}
You need to first create a mock object:
Writer mockWriter = PowerMock.createMock(Writer.class)
PowerMock.expectNew(Writer.class, "test").andReturn(mockWriter)
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!
}
}