Mock testing exceptions handling - java

Description: I want to unit test method that implements interface. Method retrieves data from files, hence potentially throws exceptions. Code that actually retrieves data is Files.readAllLines(Paths.get(fileName)); and this piece forces me to handle IOException. There are also two other classes that uses interface FileLinesReader to retrieve data. Everything works fine, code does what's expected. However when I run the whole code with invalid file name (that doesn't exist in any location), NoSuchFileException is thrown which in my understanding is thrown by Paths.get(fileName). I want to properly handle exceptions which can most likely occur during code execution.
Code:
public interface FileLinesReader {
List<String> readAllLines(String fileName);
}
public class DefaultFileLinesReader implements FileLinesReader {
String defaultFile;
public DefaultFileLinesReader(String defaultFile) {
this.defaultFile = defaultFile;
}
#Override
public List<String> readAllLines(String fileName) {
List<String> linesOutput;
try {
linesOutput = Files.readAllLines(Paths.get(fileName));
}
catch(IOException e) {
System.err.println("Provided file: \"" + fileName + "\" does not exist. Default file: " + defaultFile);
e.printStackTrace();
System.err.println("Do you want to use default file? [y/n]");
try(Scanner scanner = new Scanner(System.in)) {
while(true) {
String input = userInput(scanner);
if (input.equals("y")) {
try {
linesOutput = Files.readAllLines(Paths.get(defaultFile));
break;
}
catch (IOException ex) {
throw new RuntimeException("Default file not defined!", ex); //Should never be thrown. Default file must exist in MessageSender folder.
}
}
//Default file must exist in designated folder.
//If no file selected, execution is ceased.
if (input.equals("n")) {
throw new RuntimeException("No file selected.");
}
System.out.println("Invalid command.");
System.out.println("Do you want to use default file? [y/n]");
}
}
}
return linesOutput;
}
//Created to define input in unit tests
protected String userInput(Scanner scanner) {
return scanner.next();
}
}
Problem: I want to use Mockito to test this class but apparently I still don't understand mocking enough. I want to spy method for setting up user input, and then also by spying throw RuntimeException as I would like to cease code execution. What I get in result is like my code is executing normally, not as a test. try-catch block is executed but message I get is that NoSuchFileException has been thrown. It reaches to the moment when user can enter command, but spying method that should pass "n" as input definitely doesn't work. This is where I get really confused. I'm not sure if I catch exceptions correctly. I've tried to create separate method only for Paths.get(fileName) so I could mock it too but I got a feeling that code get's more cumbersome because of it. What I have now is code like this:
#ExtendWith(MockitoExtension.class)
class DefaultFileLinesReaderTest {
private static final String FAKE_FILE = "fakeFile";
private DefaultFileLinesReader defaultFileLinesReaderTest;
private DefaultFileLinesReader defFileLinesReaderSpy;
#BeforeEach
void setUp() {
defaultFileLinesReaderTest = new DefaultFileLinesReader(FAKE_FILE);
defFileLinesReaderSpy = spy(defaultFileLinesReaderTest);
}
#Test
void readAllLines_RuntimeException() {
Scanner scanner = mock(Scanner.class);
when(defFileLinesReaderSpy.userInput(scanner)).thenReturn("n");
when(defFileLinesReaderSpy.readAllLines(FAKE_FILE)).thenThrow(new RuntimeException());
assertThrows(RuntimeException.class, () -> defFileLinesReaderSpy.readAllLines(FAKE_FILE));
}
}
And the output when running test is like this:
Provided file: "fakeFile" does not exist. Default file: fakeFile
java.nio.file.NoSuchFileException: fakeFile
\\stacktrace here
Do you want to use default file? [y/n]
Question: How can I test if exceptions thrown in readAllLines method are handled as I expected?

Normally, you would throw a subclass of RuntimeException, you may think of using IllegalArgumetException.
For unit tests, you should have test methods to test the normal behavior of your class method and other test methods for testing thrown exceptions cases.
To test the cases, where an exception might be thrown, you can consider this example (JUnit 4):
#Rule
public ExpectedException exceptionRule = ExpectedException.none();
#Test
public void shouldThrowException() {
exceptionRule.expect(IllegalArgumentException.class);
exceptionRule.expectMessage("Your exception message");
}
And if you just want to check the thrown exception type you can simply use :
#Test(expected = IllegalArgumentException.class)
public void shouldthrowException() {
// Call method
}

Related

How test an method exception when the file exists and cannot be deleted?

I would like to test the exception trowed by this piece of code:
private void delete(final File file, final String fileName) {
boolean deleted = file.delete();
if (deleted) {
log.info("File {} was deleted", fileName);
} else {
throw new RuntimeException("The file exists but could not be deleted");
}
}
My first idea was create a temporary file that can't be deleted in my unit test. For sure, I would like to control this behavior to delete this file after the test. But I supposed that this is not possible, it's contradictory. Set the file to "read only" not works for this case.
So, any ideas?
I could create a new class and pass the responsibility to delete the file and return the boolean, so I could mock it and test my exception. But I would like to explore another possibilities using Junit and Java before do this.
I think that creating a temporary file or locking a file is overkill and not required.
The fact that a file cannot be deleted depends on runtime conditions on the filesystem where this file is hosted. You don't need to reproduce these conditions in your unit test and besides reproducing these conditions in the unit test will not guarantee you that the file used at runtime will be not deletable. So I think that this point should not be considered in the unit test.
You could simply mock/stub File.delete() to make it return false and assert that a RuntimeException is thrown : the actual behavior to assert.
With Mockito for example :
#RunWith(MockitoJUnitRunner.class)
public class FileServiceTest {
#Test(expected = RuntimeException.class)
public void myMethodToTest() throws Exception {
File file = Mockito.mock(File.class);
Mockito.when(file.delete()).thenReturn(false);
FileService.delete(file, "test");
}
}
In general I would do the following:
#RunWith(MockitoJUnitRunner.class)
public class FileServiceTest {
#Mock
private File file;
#Test
public void myMethodToTest() {
doReturn(false).when(file).delete();
try{
FileService.delete(file, "test");
fail("this test should throws an exception.")
} catch (RuntimeException e) {
assertEquals("The file exists but could not be deleted", e.getMessage());
}
}
}
On this example the test fails in case it doesn't throw any exception and, in case it gets a RuntimeException, it verify if that exception is the one you throw.
Use mock objects when writing unit tests.
Get Mockito and create a mock of the file input parameter.
Then set the return value of the delete method on your mock to return true or false based on the desired test outcome.
Use a different file and test deletion on that file. The extension could be the same for the second file, but the content and location may not need to be.

Why do I get a Reflection exception NoSuchMethodException if the tests runs fine

I'm using Reflection to Mock a private method (I don't want to discuss if that makes sense or not).
Anyone know why? I'll let my testClass source code here it may help. I've tryed much of the Internet helps and ways to solve this but none have worked for me.
public class testProtexManagerProcessRequiredFile {
#Mock
ProtexManager PxManager;
#Before
public void inicializa() {
MockitoAnnotations.initMocks(this);
}
#Test
public void processRequiredFileTest() throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
Method method;
try {
method = ProtexManager.class.getDeclaredMethod("processRequiredFile", File.class);
method.setAccessible(true);
File FileExample = new File();
String NameExample = "Nome";
File outputs = new File();
outputs = (File) Mockito.when(method.invoke(PxManager, FileExample,NameExample)).thenReturn(FileExample);
assertNotNull(outputs);
assertEquals(outputs, method.invoke(PxManager, FileExample,NameExample));
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Teste Concluido.");
}
}
That's the method code:
private File processRequiredFile(File file, String name) {
if (!file.exists()) {
this.message = name + " file not found at location: " + file;
this.msgResponse.addMsgList(MsgCode.FAILURE, MsgLevel.ERROR, this.message, StringUtils.EMPTY);
}
return file;
}
And thank you all for helping me in my doubts.
To answer your question,
Because you caught the NoSuchMethodException. To get a test failure you have to somehow get some exception or error during the test execution
To follow up on the comments, here's how one can test this method:
// let's assume there are getter for this.message / this.msgResponse
// and this method is in the class foo.bar.Foobar
protected File processRequiredFile(File file, String name) {
if (!file.exists()) {
this.message = name + " file not found at location: " + file;
this.msgResponse.addMsgList(MsgCode.FAILURE, MsgLevel.ERROR, this.message, StringUtils.EMPTY);
}
return file;
}
In a test class foo.bar.FoobarTest:
#Mock
private File file;
private Foobar foobar = new Foobar();
#Test
public void testWithNonExistingFile() {
Mockito.when(this.file.exists()).thenReturn(false); // this is to illustrate, you could also use some non existent file: new File("/does-not-exists.foo")
File result = this.foobar.processRequiredFile(this.file, "some name");
assertThat(result).isEqualTo(this.file);
assertThat(foobar.getMsgResponse()).isNotEmpty(); // TODO: better assertion
assertThat(foobar.getMessage()).isEqualTo( "some name file not found at location: " + this.file);
}
#Test
public void testWithExistingFile() {
Mockito.when(this.file.exists()).thenReturn(true);
File result = this.foobar.processRequiredFile(this.file, "some name");
assertThat(result).isEqualTo(this.file);
assertThat(foobar.getMsgResponse()).isEmpty();
assertThat(foobar.getMessage()).isNull();
}
The class under test (i.e. Foobar) is really tested, this uses a real instance of it and call its method. A mock is used to replace something we don't have (here it's a file to illustrate but it's usually something more complicated)
What is your actual question? Why the testcase succeeds? That's already answered in the comments. You catch the exception and essentially ignore it. If you want to see the stacktrace on STDERR and let the testcase fail, you have to initiate the failing procedure yourself, e.g by calling
throw (AssertionFailedError) new AssertionFailedError("method not found").initCause(e);
This construct looks strange but JUnit 3 (I assume you're using that given your code) doesn't come with an AssertionFailedError with a constructor allowing to pass a cause. This way you see the stacktrace in your IDE as well and will be visible in JUnit-reports created during build processes.
Or is your question why the particular method is not found? One reason can be that someClass.getDeclaredMethod only returns a result if the method is declared in that particular class. If that class has a super class inheriting this method, you have to use the superclass when calling getDeclaredMethod to get the method.
If you don't know what class actually contains a method you have to iterate over all superclasses until reaching the "end":
Class<?> clazz = ProtexManager.class;
while (clazz != null) {
try {
return clazz.getDeclaredMethod("processRequiredFile", File.class);
catch(NoSuchMethodException e) {
clazz = clazz.getSuperClass();
}
}
That code block swallows the NoSuchMethodException but I don't want to do things more complicated than necessary to illustrate the idea.
Another reason why the method is not found might be that the class in question has a method processRequiredFile(java.io.File) and not processRequiredFile(com.blackducksoftware.sdk.codecenter.deeplicense.data.File). Also you later call the method by method.invoke using three parameters (PxManager, File, String) so either your call of getDeclaredMethod is missing parameter classes or your call of invoke will later fail due to the differences between declaration of the method and passed parameters.

How to test this file-writing, 3lines function?

This is my method in some service class. It's public so it should be tested. I simply do not know WHAT should I test. I'd mock Writer and spyOn function call, but with this implementation it's impossible (isn't it?)
I'm using Mockito and JUnit
For now, I can only make function to throw and assert that exception
Any help?
#Override
public void initIndexFile(File emptyIndexFile) {
try {
Writer writer = new FileWriter(emptyIndexFile);
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
If you feel that adding the the special content is the business logic and therefore the responsibility of your class, then creating the FileWriter is not (according to the single responsibility pattern.
So you should use a FileWriterFactory that is injected into your Class under Test. Then you can mock that FileWriterFactory to return a mock implementation of the Writer interface on which in turn you can check that it got the expected String.
Your CuT would change to this:
private final WriterFactory writerFactory;
public ClassUnderTest(#Inject WriterFactory writerFactory){
this.writerFactory = writerFactory;
}
#Override
public void initIndexFile(File emptyIndexFile) {
try {
Writer writer = writerFactory.create(emptyIndexFile);
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
and your test to this:
class Test{
#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private FileWriterFactory fileWriterFactory;
private Writer fileWriter = spy(new StringWriter());
File anyValidFile = new File(".");
#Test
public void initIndexFile_validFile_addsEmptyraces(){
//arrange
doReturn(fileWriter).when(fileWriterFactory).create(any(File.class));
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
//assert
verify(fileWriterFactory)create(anyValidFile);
assertEquals("text written to File", "[]", fileWriter.toString());
verify(fileWriter).close();
}
}
in addition you could easily check that your CuT intercepts the IOException:
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void initIndexFile_missingFile_IndexFileInitializationException(){
//arrange
doReturnThrow(new IOException("UnitTest")).when(fileWriterFactory).create(any(File.class));
//assert
exception.expect(IndexFileInitializationException.class);
exception.expectMessage("Error initialization index file "+anyValidFile.getPath());
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
}
Nice! a factory just to test 3 lines of code! – Nicolas Filotto
This is a good point.
The question is: will there be any method within that class ever interacting with the File object directly and needs to create the FileWriter afterwards?
If the answer is "no" (as it is most likely) following the KISS principle you should inject a Writer object directly instead of the factory and have your methods without the File parameter.
private final Writer writer;
public ClassUnderTest(#Inject Writer writer){
this.writer = writer;
}
#Override
public void initIndexFile() {
try {
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
modified test:
class Test{
#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Rule public ExpectedException exception = ExpectedException.none();
#Mock
private FileWriterFactory fileWriterFactory;
#Mock
private Writer failingFileWriter;
private Writer validFileWriter = spy(new StringWriter());
File anyValidFile = new File(".");
#Test
public void initIndexFile_validFile_addsEmptyraces(){
//arrange
// act
new ClassUnderTest(validFileWriter).initIndexFile();
//assert
verify(fileWriterFactory)create(anyValidFile);
assertEquals("text written to File", "[]", fileWriter.toString());
verify(fileWriter).close();
}
#Test
public void initIndexFile_missingFile_IndexFileInitializationException(){
//arrange
doReturnThrow(new IOException("UnitTest")).when(failingFileWriter).write(anyString());
//assert
exception.expect(IndexFileInitializationException.class);
exception.expectMessage("Error initialization index file "+anyValidFile.getPath());
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
}
}
To test that your method can interact with a writer correctly, by sending the correct commands, your pogram has to expose some sort of "seam" so that your test can configure a mock FileWriter. I'm not familiar with mockito but one way would be to encapsulate the FileWriter instantiation behind a method then your test could override that method to return a mock FileWriter.
Assuming that File is an interface:
public Writer getFileWriter(File emptyIndexFile) {
return new FileWriter(emptyIndexFile);
}
This could allow you to override the above method for a test and return a fake Writer
#Override
public Writer getFileWriter(File emptyIndexFile) {
return mockFileWriterInstance;
}
Then your test could make exercise initIndexFile and make assertions on the operations. Using a mock file writer shoudl be trivial to throw IOException so that you can exercise error handling logic.
You could simply provide a temporary file to your method in your test and simply check that it contains [] as expected and once over delete the file.
Something like:
public class FileWritingTest {
// File to provide to the method initIndexFile
private File file;
/* This is executed before the test */
#Before
public void init() throws IOException {
// Create a temporary file
this.file = File.createTempFile("FileWritingTest", "tmp");
// Indicates that it should be removed on exit
file.deleteOnExit();
}
/* This is executed after the test */
#After
public void clean() throws IOException {
// Delete the file once test over
file.delete();
}
#Test
public void testInitIndexFile() throws IOException {
FileWriting fw = new FileWriting();
// Call the method
fw.initIndexFile(this.file);
// Check that the content is [] as expected
Assert.assertEquals("[]", new String(Files.readAllBytes(file.toPath())));
}
}
NB 1: I rely on new String(byte[]) which means that I rely on the default character encoding like you do in your current code but it is not a good practice, we should set a character encoding explicitly to avoid platform dependent.
NB 2: Assuming that you use java 7 or higher, you should consider using the try-with-resources statement to properly close your writer, your code would then be:
public void initIndexFile(File emptyIndexFile) {
try (Writer writer = new FileWriter(emptyIndexFile)) {
writer.write("[]");
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
Mocking a dependency is possible and natural, but mocking an object declared in the body of the method is not natural and tricky.
I imagine 3 solutions:
1) Why, instead of mocking, could you not simply assert that the file is written with the expected character?
It avoids tricks, but it may be redundant and slow if you perform this task very often and you want to unit test them.
2) Making the local variable an instance field to mock it. This seems really a not at all clean solution. If you have multiple methods in the same class that does this kind of processing, you risk to reuse the same writer or to have multiple writer fields. In both cases, you could have side effects.
3) If you perform many write operations and you want to really isolate the call to the writer, you have a solution: redesign your code to have a testable class.
You could extract a dependency to perform the writer processings. The class could provide a method with required parameters to perform instructions. We could call it : WriteService.
public class WriteService {
...
public void writeAndClose(Writer writer, String message){
try {
writer.write(message);
writer.close();
}
catch (IOException e) {
throw new IndexFileInitializationException("Error initialization index file " + emptyIndexFile.getPath());
}
}
}
This class is testable because the writer dependency is a parameter.
And you call the new service like that :
public class YourAppClass{
private WriteService writeService;
public YourAppClass(WriteService writeService){
this.writeService=writeService;
}
#Override
public void initIndexFile(File emptyIndexFile) {
Writer writer = new FileWriter(emptyIndexFile);
writeService.writeAndClose(writer,"[]");
}
}
Now initIndexFile() is also testable by mocking WriteService.
You could check tat writeAndClose() is called on writeService with the good parameter.
Personally, I would use the first solution or the third solution.

Writing Junit test to cover exception and catch block

I have written Junit test case for following function. When checked JACOCO test coverage. It is showing only try block is covered by test case. I am newbie to writing test cases. How the exceptions and catch block can be covered in test cases
Here is a method
public static List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
//logic to read file
}
catch (Exception e)
{
System.out.println("Error in CsvFileReader !!!");
e.printStackTrace();
} finally
{
try
{
fileReader.close();
} catch (IOException e)
{
System.out.println("Error while closing fileReader !!!");
e.printStackTrace();
}
}
return students;
}
And TestMethod
#Test
public void ReadCsvFileTest()
{
String fileName = "test.csv";
List<Student> result = new ArrayList<Student>();
result = CsvFileReader.readCsvFile(fileName);
Student student1 = null;
Iterator<Student> it = result.iterator();
while (it.hasNext())
{
Student s = it.next();
if ("471908US".equals(s.getId()))
{
student1 = s;
break;
}
}
assertTrue(student1 != null);
}
In such situations you may often consider the introduction of additional dependencies to your class. Here is what I mean with a rough example. Create a factory for readers:
interface BufferedReaderFactory
{
public BufferedReader createBufferedReader(String fileName) throws IOException;
}
Then you will have a trivial implementation that hardly needs any testing, e.g. something similar:
class BufferedReaderFactoryImpl implements BufferedReaderFactory
{
#Override
public BufferedReader createBufferedReader(String fileName) throws IOException
{
return new BufferedReader(new FileReader(fileName));
}
}
Then you have to find a way to inject this dependency into your class. I usually use Guice in my daily work but you may try something as simple as using constructor injection and making your method non static. Here is an example:
class CsvFileReader
{
private final BufferedReaderFactory factory;
public CsvFileReader(BufferedReaderFactory factory)
{
this.factory = factory;
}
public List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
try
{
fileReader = factory.createBufferedReader(fileName);
...
}
catch(IOException e)
{
...
}
finally
{
...
}
return new LinkedList<>();
}
}
With a mocking framework like Mockito the behavior of this class in case of IOException-s is easier to test now (note that you may also return mocks that throw exceptions from the factory). Here is a sample:
#RunWith(MockitoJUnitRunner.class)
public class MyTest
{
#Mock
private BufferedReaderFactory mockFactroy;
#Test
public void testIOException() throws IOException
{
String ivalidFileName = "invalid.txt";
//throw exception in case that invalid file name is passed to the factory
Mockito.when(mockFactroy.createBufferedReader(ivalidFileName)).thenThrow(new IOException("Hello!"));
CsvFileReader csvFileReader = new CsvFileReader(mockFactroy);
//invoke with a factory that throws exceptions
csvFileReader.readCsvFile(ivalidFileName);
//...
//and make a sensible test here, e.g. check that empty list is returned, or proper message is logged, etc.
}
}
You may do that without Mockito, of course - by implementing a test factory. But this is more cumbersome especially in more complicated use cases. Once the IOException is thrown you will get appropriate coverage report by JaCoCo.
Also mind a limitation of JaCoCo mentioned here, in section Source code lines with exceptions show no coverage. Why?
Given the current signature of your method under test, getting to full coverage isn't easy: your catch block is only executed when an exception is thrown within your try block.
One way to solve this: do not pass in the file name, but the reader object itself. Like:
public static List<Student> readCsvFile(String fileName) {
return readCsvFile(new BufferedReader(fileName));
}
static List<Student> readCsvFile(BufferedReader reader) {
try {
...
} catch( ...
Now you can write several specific unit tests for that second method. You keep your tests that simply do "correct" reading; but you add one where you pass in a mocked reader object ... that simply throws an Exception at some point. Please note that I made that new method just package protected - you probably don't want to use that "public"; and making it private would prevent it from being unit tested.
That should help you achieving full coverage. Of course you will also need at least one test to "cover" the string-taking method, too.
Some notes:
Be careful about re-inventing the wheel. There are many existing CSV parsers already. And be assured: writing a correct CSV parser that is able to deal with all "correct" input CSV is much harder than it sounds. If this is not for "learning purposes" I strongly advise to not write your own CSV parser.
Be careful about making such things static. As said, a real CSV parser is a complicated thing, and worth its complete own class. So no static helper methods - a normal class which you instantiate to then call methods on it (that would also for using dependency injection which would help with the problem you are asking about ... getting exceptions thrown within try blocks)
You are catching Exception in your code example. Don't do that - try to catch exactly those exceptions that your code can actually produce (probably IOException in your case).

Mark unit test as an expected failure in JUnit

How can I mark a test as an expected failure in JUnit 4?
In this case I want to continue to run this test until something is patched upstream. Ignoring the test goes a little too far, as then I might forget about it. I may be able to add an #expected annotation and catch the exception thrown by assertThat, but that also seems to lie about the expected behavior.
Here's what my current test looks like:
#Test
public void unmarshalledDocumentHasExpectedValue()
{
doc = unmarshaller.unmarshal(getResourceAsStream("mydoc.xml"));
final ST title = doc.getTitle();
assertThat(doc.getTitle().toStringContent(), equalTo("Expected"));
}
That assert should succeed, but because of an upstream bug it doesn't. Yet, that test is correct; it should succeed. Virtually all the alternatives that I've found are misleading. Right now I think #Ignore("This test should pass once fixed upstream") is my best bet, but I still have to remember to come back to it. I'd prefer that the test run.
In Python I can use the expectedFailure decorator:
class ExpectedFailureTestCase(unittest.TestCase):
#unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
With Qt's QTestLib in C++, you can use QEXPECT_FAIL:
QEXPECT_FAIL("", "Will be fixed next version", Continue);
QCOMPARE(i, 42);
In both cases above, the unit test runs which is what I'm hoping to have happen. Am I missing something in JUnit?
I'm not quite getting the specifics of your scenario, but here's how I generally test for expected failure:
The slick new way:
#Test(expected=NullPointerException.class)
public void expectedFailure() {
Object o = null;
o.toString();
}
for older versions of JUnit:
public void testExpectedFailure() {
try {
Object o = null;
o.toString();
fail("shouldn't get here");
}
catch (NullPointerException e) {
// expected
}
}
If you have a bunch of things that you want to ensure throw an exception, you may also want to use this second technique inside a loop rather than creating a separate test method for each case. If you were just to loop through a bunch of cases in a single method using expected, the first one to throw an exception would end the test, and the subsequent cases wouldn't get checked.
What about explicitly expecting an AssertionError?
#Test(expected = AssertionError.class)
public void unmarshalledDocumentHasExpectedValue() {
// ...
}
If you're reasonably confident that only the JUnit machinery within the test would raise AssertionError, this seems as self-documenting as anything.
You'd still run the risk of forgetting about such a test. I wouldn't let such tests into version control for long, if ever.
I'm assuming here that you want the test to pass if your assert fails, but if the assert succeeds, then the test should pass as well.
The easiest way to do this is to use a TestRule. TestRule gives the opportunity to execute code before and after a test method is run. Here is an example:
public class ExpectedFailureTest {
public class ExpectedFailure implements TestRule {
public Statement apply(Statement base, Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Throwable e) {
if (description.getAnnotation(Deprecated.class) != null) {
// you can do whatever you like here.
System.err.println("test failed, but that's ok:");
} else {
throw e;
}
}
}
};
}
}
#Rule public ExpectedFailure expectedFailure = new ExpectedFailure();
// actually fails, but we catch the exception and make the test pass.
#Deprecated
#Test public void testExpectedFailure() {
Object o = null;
o.equals("foo");
}
// fails
#Test public void testExpectedFailure2() {
Object o = null;
o.equals("foo");
}
}
First, note that the first method is marked as #Deprecated. I'm using this as a marker for the method for which I want to ignore any assertion failures. You can do whatever you like to identify the methods, this is just an example.
Next, in the ExpectedFailure#apply(), when I do the base.evaluate(), I'm catching any Throwable (which includes AssertionError) and if the method is marked with the annotation #Deprecated, I ignore the error. You can perform whatever logic you like to decide whether you should ignore the error or not, based on version number, some text, etc. You can also pass a dynamically determined flag into ExpectedFailure to allow it to fail for certain version numbers:
public void unmarshalledDocumentHasExpectedValue() {
doc = unmarshaller.unmarshal(getResourceAsStream("mydoc.xml"));
expectedFailure.setExpectedFailure(doc.getVersionNumber() < 3000);
final ST title = doc.getTitle();
assertThat(doc.getTitle().toStringContent(), equalTo("Expected"));
}
For further examples, see ExternalResource, and ExpectedException
Ignoring an expected failure test rather than passing it
If you want to mark you tests as Ignored rather than Success, it becomes a bit more complex, because tests are ignored before they are executed, so you have to retrospectively mark a test as ignored, which would involve constructing your own Runner. To give you a start, see my answer to How to define JUnit method rule in a suite?. Or ask another question.
One option is mark the test as #Ignore and put text in there that is a bug perhaps and awaiting a fix. That way it won't run. It will then become skipped. You could also make use of the extensions to suit your need in a potentially different way.
I've taken Matthew's answer a step further and actually implemented an #Optional annotation you could use instead of the #Deprecated marker annotation he mentions in his answer. Although simple, I'll share the code with you, maybe it's of help for someone:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Optional {
/**
* Specify a Throwable, to cause a test method to succeed even if an exception
* of the specified class is thrown by the method.
*/
Class<? extends Throwable>[] exception();
}
With a simple alteration of Matt's ExpectedFailure class:
public class ExpectedFailure implements TestRule {
#Override
public Statement apply(final Statement base, final Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Throwable e) {
// check for certain exception types
Optional annon = description.getAnnotation(Optional.class);
if (annon != null && ArrayUtils.contains(annon.exception(), e.getClass())) {
// ok
} else {
throw e;
}
}
}
};
}
}
You can now annotate your test method with #Optional and it will not fail, even if the given type of exception is raised (provide one or more types you would like the test method to pass):
public class ExpectedFailureTest {
#Rule public ExpectedFailure expectedFailure = new ExpectedFailure();
// actually fails, but we catch the exception and make the test pass.
#Optional(exception = NullPointerException.class)
#Test public void testExpectedFailure() {
Object o = null;
o.equals("foo");
}
}
[UPDATE]
You could also rewrite your tests using JUnit's org.junit.Assume instead of the tradtional org.junit.Assert, if you want your tests to pass even if the assumption does not hold.
From Assume's JavaDoc:
A set of methods useful for stating assumptions about the conditions in which a test is meaningful.A failed assumption does not mean the code is broken, but that the test provides no useful information. The default JUnit runner treats tests with failing assumptions as ignored.
Assume is available since JUnit 4.4
Use mocked upstream class if possible. Stub it with correct result. Optionally, replace mock with real object after bug is fixed.

Categories