2

I'm having an issue with a test case.

The method being tested has a try / catch that catches a MalformedURLException but during testing I get a failure due to a Junit AssertionError that expects a MalformedURLException. But I can't find out what it is actually throwing! Here is my code (created as a MWE in eclipse).

My method that I want to test

public void throwMalFormedURLException(){

    String s = new String("www.google.com");

    try{
        url = new URL(s);
    }catch (Exception e){
        e.printStackTrace();
        e.getClass();
        e.getCause();
    }


}

the test method

@Test (expected = MalformedURLException.class)
public void testThrowMalFormedURLException() {
    MWE testClass = new MWE();
    testClass.throwMalFormedURLException();
    System.out.println("End of test");
}

This is the output in the console

End of test error details are: java.net.MalformedURLException: no protocol: www.google.com
at java.net.URL.(URL.java:593)
at java.net.URL.(URL.java:490)
at java.net.URL.(URL.java:439)
at MWE.throwMalFormedURLException(MWE.java:12)
at testMWE.testThrowMalFormedURLException(testMWE.java:12)

In the Junit console it says :

java.lang.AssertionError: Expected exception: Java.net.MalformedURLException

But the Junit is reporting failure, even though the console is telling me I've got a MalformedURLException.

What am I doing wrong with this test ?

Thanks for your thoughts.

David

DaveM
  • 734
  • 4
  • 11
  • 35

3 Answers3

8

You are catching the exception and therefore it is not being thrown.

If your intent is to test that you are capturing the exception and relaying it back to the 'user' properly, you should create tests for that specifically. You probably don't want UI elements in your unit tests* so this is a place where abstraction and DI have a lot of value. One simple approach is to create a mock object that will listen for the error message and mark a flag when the message is received. Your unit test trigger the error and then pass if the flag is set. You should also have a negative test or assert that the flag is not set prior to throwing the exception.

*Testing the UI is also a good idea but it can be a little slow. There are various tools for automating that. It generally falls in to a different phase of testing. You really want the unit tests to be really fast so that you can run them very frequently.

JimmyJames
  • 1,356
  • 1
  • 12
  • 24
  • OK so I feel very silly, how do I test that that is actually occurring ? As I want to be sure that these types of errors are caught, so as I can then send an appropriate message to my user. – DaveM Aug 10 '16 at 16:45
  • It's hard to give an exact answer since I don't know the details of the application but in a nutshell, I would create a test for the 'sending the error message to the user' when the exception is caught. For good measure, write a test that verifies no such message is sent when the exception is not thrown. – JimmyJames Aug 10 '16 at 17:25
  • if you add that as a set of points into your reply I'll accept your solution. I must admit that this is the top end of my system, but I could easily catch the error in the 'message window' that should be created. Thanks – DaveM Aug 10 '16 at 18:00
  • I added more detail. If you aren't sure about how to do the mocking, I can throw together an example later. – JimmyJames Aug 10 '16 at 18:45
  • Thanks for the extra info. I'll looking into mocking later on, but for now I have enough to sort out my problem. I'm going to catch the method in anther 'show a message to the user' class, which can be called from my catch, and pass the required exception to it, and the message (both of which I can then test for). – DaveM Aug 10 '16 at 20:28
2

You have written production code that simply isn't testable. And more specifically: this code doesn't have any "programmatically" observable side effects in the first place.

Meaning: when you write "production code", the code within a method can do three things that could be observed:

  1. Make method calls on objects that are fields of the class under test
  2. Return some value
  3. Throw an exception

For each of these options, you might be able to write testing code:

  1. Using dependency injection, you can put a mocked object into your class under test. And then you can check that the expected methods are invoked on your mock object; with the parameter that you would expect.
  2. You compare the result you get from calling that method with some expected value
  3. You use expected to ensure that a specific exception was thrown

When you look at your code; you can see: it does none of that. It only operates on objects that are created within the method. It doesn't return any value. And, most importantly: it doesn't throw an exception!

Long story short: a caught exception isn't "leaving" the method. It is caught there, and the method ends normally. The fact that you print details about the caught exception doesn't change that.

So, the first thing you have to do: remove the whole try/catch from your production code!

And, if you want to have a more specific test, you can do something like:

@Test
public void testException() {
  try { 
    new MWE().throwMalFormedURLException();
    fail("should have thrown!");
  } catch ( MalFormedURLException me ) {
    assertThat(me.getMessage(), containsString("whatever"));
  }

The above:

  1. fails when no exception is thrown
  2. fails when any other exception than MalFormedURLException is thrown
  3. allows you to check further properties of the thrown exception
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Thanks for the reply, I'm giving you a +1 as this is where I had already going to in my code, after the response from @Jimmy above. Thanks for the input, as its made my final code cleaner by following your example. – DaveM Aug 10 '16 at 20:30
0

This is a valid test failure. The test asserts that calling throwMalFormedURLException() will throw MalformedURLException, but since you're catching the exception, it doesn't throw it, so the test fails.

David Moles
  • 48,006
  • 27
  • 136
  • 235