1

I have a JUnit test where I am testing a method with null arguments. If the argument /arguments are null, then I would throw a NullPointerException. The method by itself will just throw an IOException. I am using doThrow on mock object of the class but it seems to me that I am losing the exception in the doThrow() construct and I am unable to catch it. Additionally, I strictly do not want to use a try catch in my unit tests. So I am using @Rules for the same. Here is the code snippet:

public class TableTest {

@Rule
public ExpectedException exception = ExpectedException.none();
private static Table spyTable;

@Test
public void testCreateTableWithNullTableName_throwsIOEXception() throws IOException {
    final String tableName = null;
    mockConfig = mock(Configuration.class);
    spyPersonTable = spy(new PersonTable());
    doThrow(new IllegalArgumentException()).when(spyPersonTable).createTable(tableName, mockConfig);
    // exception.expect(IllegalArgumentException.class);
}

Using the @rule's exception object, If I use the commented line to catch my exception, the exception created in the doThrow() construct will be lost and I cannot catch it. My unit test will fail and complain that:

Expected test to throw an instance of java.lang.IllegalArgumentException

The test works fine if I the line is commented as I have it. Here is how my method looks like that I am trying to test:

public void createTable(final String tableName, final Configuration config) throws IOException 

The method needs to throw IOException since the specific exception thrown during table creation is a subclass of IOException.

Is my catching of exception in the JUnit test wrong for this type of checking exception.

noobcoder
  • 11,983
  • 10
  • 39
  • 62
  • " I strictly do not want to use a try catch in my unit tests" <-- why, if it can cure the problem? – fge Mar 19 '14 at 05:55
  • Just a requirement that is given to me. – noobcoder Mar 19 '14 at 05:56
  • And is there an exception which is actually thrown? – fge Mar 19 '14 at 05:57
  • Well, the method "createTable" just does not throw the IllegalArgumentException specifically. At the beginning of the method body, I have Preconditions that check for null and so on – noobcoder Mar 19 '14 at 05:59
  • And the precondition is what? If `.checkNotNull()` it will throw an NPE on failure, not an `IllegalArgumentException`. You shouldn't have to `.doThrow()` if this is the case, not even `spy()` is since this is the only thing you want to test – fge Mar 19 '14 at 06:01
  • This is how I am doing it: Preconditions.checkArgument(!(tableName.isEmpty()) || (!(tableName.equals(null))), "tableName cannot be null or empty"); – noobcoder Mar 19 '14 at 06:03
  • OK, why don't you just create a normal `PersonTable` and call `table.createTable(null, null);`? If your construct above can actually capture the IllegalArgumentException then it should work. – fge Mar 19 '14 at 06:08
  • Also, your test name seems to imply that you expect this operation to throw an IOException. Uh. So, what is what? – fge Mar 19 '14 at 06:09
  • doThrow needs a mock object. It will not allow me to call the method createTable with a real object – noobcoder Mar 19 '14 at 06:10
  • Yes, and that is a mistake imho. You want to create the behaviour of `createTable` so why mock its behaviour?? – fge Mar 19 '14 at 06:11
  • so what do you suggest doing ? I am kinda confused here – noobcoder Mar 19 '14 at 06:12
  • See my answer; I have always done this way and it has always worked for me... I don't know how to use your `@Rule`, so you'll have to adapt that code. – fge Mar 19 '14 at 06:16

2 Answers2

3

You seem to want to test whether that method throws an exception; but by using doThrow().when(sut).createTable(...) you prevent the SUT from having its normal behaviour.

Just do:

final PersonTable table = new PersonTable();
table.createTable(null, null); // theoretically throws IllegalArgumentException

And just check that there is a thrown exception. No idea how you do that with your @Rule but I don't use that and here how I'd do that:

final PersonTable table = new PersonTable();
try {
    table.createTable(null, null);
    fail("No exception thrown!");
} catch (IllegalArgumentException e) {
    assertEquals(e.getMessage(), "the expected message");
}
fge
  • 119,121
  • 33
  • 254
  • 329
  • This test still fails but I get the idea behind it and can take it from here. I still dont understand that when I say something like : public void someTestName() throws IOException{ } but I also throw an IllegalArgumentException if I pass null arguments. Should I also throw this exception too in the testName declaration ? – noobcoder Mar 19 '14 at 06:24
  • You needn't do that, since `IllegalArgumentException` is an unchecked exception – fge Mar 19 '14 at 06:25
  • the reason for throwing IOException is that, my createTable method throws an IOException and any test method that makes calls to this method need to throw an IOException – noobcoder Mar 19 '14 at 06:25
  • Then don't use `Preconditions` ;) – fge Mar 19 '14 at 06:25
  • damn....dont want to have so many design changes when i have stuff to be reviewed..!!! – noobcoder Mar 19 '14 at 06:26
  • OK, what if you posted another question with your code, what you expect and what happens instead? – fge Mar 19 '14 at 06:31
  • nope. Will not do that. I don't doubt your answers bro. I just wanted to check if I am wrong here as I am new to the mocking framework. – noobcoder Mar 19 '14 at 06:37
  • Well, using `Preconditions` will throw an IllegalArgumentException; you should not use it if you want `IOException` to be thrown in all cases... – fge Mar 19 '14 at 06:42
  • You may want to have a look to this answer : http://stackoverflow.com/questions/17426313/how-to-verify-that-an-exception-was-not-thrown/17428439#17428439 – bric3 Mar 19 '14 at 10:13
0

Your issue is that your test doesn't actually call createTable, and therefore there's nothing to throw the exception. You could write this.

@Test
public void testCreateTableWithNullTableName_throwsIOEXception() throws IOException {
    final String tableName = null;
    mockConfig = mock(Configuration.class);
    spyPersonTable = spy(new PersonTable());
    doThrow(new IllegalArgumentException()).when(spyPersonTable).createTable(tableName, mockConfig);
    exception.expect(IllegalArgumentException.class);
    spyPersonTable.createTable(tableName, mockConfig);
}

But I don't really see the point of doing that, because you'd really just be testing Mockito. You're stubbing the method so that it does nothing other than throwing IllegalArgumentException, then checking that your stubbing worked OK - which seems pointless to me.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110