JAVA: JUnitTest, testing method that throws two exceptions - java

I'm testing a method that throws two different exceptions. This is my header:
#Test (expected = A8InvalidInputException.class)
public void testGuessCharacter() throws A8InvalidInputException, A8AlreadyGuessedException
{
...
}
The body has two try/catch blocks (a search on SO resulted in a post that said that's how you test that exceptions are thrown), one for each exception. It seems to me I should break this up into two test methods, especially because I can only have one expected attribute. However, when I do that, the method that is supposed to be testing A8InvalidInputException is requiring a try/catch for A8AlreadyGuessedException, and the method that is supposed to be testing A8AlreadyGuessedException is requiring a try/catch for A8InvalidInputException. I'm not really sure how to write this test. This is the method I'm trying to test:
/**
* This method returns whether a specified character exists in the keyPhrase field
* #param guess a character being checked for in the keyPhrase field
* #return returns whether a specified character exists in the keyPhrase field
* #throws A8InvalidInputException if a non-valid character is passed as input to this method
* #throws A8AlreadyGuessedException if a valid character which has already been guessed is passed as input to this method
*/
public boolean guessCharacter(char guess) throws A8InvalidInputException, A8AlreadyGuessedException
{
if(isValidCharacter(guess))
{
guess = Character.toLowerCase(guess);
if(guessedCharacters.contains(guess) )
{
throw new A8AlreadyGuessedException("" + guess);
}
else
{
guessedCharacters.add(guess);
if(keyPhrase.contains("" + guess))
return true;
else
{
numberOfGuessesLeft--;
return false;
}
}
}
else
{
throw new A8InvalidInputException("" + guess);
}
}

Just add both exceptions in the throws clause:
#Test (expected = A8InvalidCharacterException.class)
public void testInvalidCharacterScenario() throws A8InvalidInputException, A8AlreadyGuessedException { ... }
#Test (expected = A8InvalidInputException.class)
public void testInvalidInputScenario() throws A8InvalidInputException, A8AlreadyGuessedException { ... }
Then, if one test throws the other exception (the unexpected one) then your test will automatically fail.

A method can throw only one exception when it is ran. That’s why there can be only one expected attributed.
You may want to have three test cases: one when the method throws one exception, one when the method throws the other, and one when the method doesn’t throw any exception at all.
Do not put any try/catch statement in your #Test methods, just declare that they throw exceptions.

Yes, you should break this up into two unit tests. One with an invalid input to trigger the A8InvalidInputException and another with an "already guessed" input to trigger the A8AlreadyGuessedException.

Consider, to make writing your tests a little simpler, breaking them out into two distinct portions - testing isValidCharacter and testing guessCharacter separately.
Presuming that isValidCharacter(guess) will fail if you receive an invalid guess, I think that throwing A8InvalidInputException in that method would be ideal.
public boolean isValidCharacter(char guess) throws A8InvalidInputException {
// have your logic to check the guess, and if it's invalid, throw
}
Then all you would need to do is test that particular method to see if it throws the exception on bogus input.
#Test (expected = A8InvalidInputException.class)
public void testIsValidCharacterWithInvalidCharacter() {
// write your test here.
}
Next, you can change your method to only care about the happy-path of the isValidCharacter method, since if you don't return the boolean, you've thrown the exception.
Lastly, you would only concern the tests for guessCharacter around whether or not it throws A8AlreadyGuessedException.
#Test (expected = A8AlreadyGuessedException.class)
public void testGuessCharacterWithAlreadyGuessedValue() {
// write your test here.
}

Related

Understanding Exception tests in JUnit 5

I've been away from Java for a while and JUnit 5 has come along in the meantime. I'm trying to rewrite some existing JUnit 4 tests as JUnit 5 but I'm struggling with how to handle methods that throw exceptions. For example, I have a class I wrote called LocalizationUtils (not to be confused with any other class by that name found in the API) and it has a method called getResources() which wants a non-null base name as an input and returns the corresponding resource bundle if it can be found. It throws a NullPointerException if the base name is null and a MissingResourceException if the resource bundle can't be located. Here's the code:
static public ResourceBundle getResources(String baseName) throws NullPointerException, MissingResourceException {
if (baseName == null) {
final String msg = "The base name cannot be null.";
NullPointerException npExcp = new NullPointerException(msg);
Logger logger = Logger.getLogger(CLASS_NAME);
logger.log(Level.SEVERE, msg, npExcp);
throw npExcp;
}
/* Get the resource bundle for the current locale. */
try {
return(ResourceBundle.getBundle(baseName));
}
catch (MissingResourceException mrExcp) {
String msg = "Unable to find resources for base name " + baseName + ".";
Logger logger = Logger.getLogger(CLASS_NAME);
logger.log(Level.SEVERE, msg, mrExcp);
throw mrExcp;
}
}
The manual for JUnit 5 gives this example of handling an exception:
#Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
This example makes no sense to me. It seems to be creating an exception with the message "a message" out of thin air. I don't see that it is actually executing ANY class or method at all. There's no real explanation of how this is supposed to work so maybe/probably it's just a misunderstanding on my part.
In an attempt to write a test that actually executes my code and forces both errors to occur, I wrote this JUnit 5 test:
#Test
void testGetResourcesString() {
//Normal cases
//Exception - input parameter is null
/* Verify that the expected type of exception was thrown. */
Throwable npExcp = assertThrows(NullPointerException.class, () -> {
LocalizationUtils.getResources(null);
});
/* Verify that the exact right error message was generated. */
assertEquals("The base name cannot be null.", npExcp.getMessage());
//Exception - all input is non-null but resource bundle not found
/* Verify that the expected type of exception was thrown. */
Throwable mrExcp = assertThrows(MissingResourceException.class, () -> {
LocalizationUtils.getResources("foo");
});
/* Verify that the exact right error message was generated. */
assertEquals("Can't find bundle for base name foo, locale en_CA", mrExcp.getMessage());
}
Everything in this test works as I would expect and it seems more logical than the example in the manual since it actually executes a class and method to cause the exception to be thrown. I also wanted to be sure that exactly the right message was generated since it's easy to imagine a method with several input parameters instead of just one where any of the parameters being null should throw an exception with a unique message. Simply determining that the right exception was thrown doesn't seem like enough to me: I feel that I want to be sure that the right message was created so that I know the code is reacting to the right null parameter out of several.
I'm reluctant to believe the manual is wrong though; I imagine a team of several experienced developers wrote it and were very careful. Can anyone with more JUnit 5 knowledge than me - which must be just about everyone who has ever used JUnit 5 - confirm that the example in the manual is correct and, if it is, how it is supposed to work when no class or method name is provided?
The example does call a "method". The lambda expression () -> {...} defines an anonymous method. It contains one statement: throw new IllegalArgumentException.
Refer to the java language specs
() -> {} // No parameters; result is void
() -> 42 // No parameters, expression body
() -> null // No parameters, expression body
() -> { return 42; } // No parameters, block body with return
() -> { System.gc(); } // No parameters, void block body
(int x) -> x+1 // Single declared-type parameter
(int x) -> { return x+1; } // Single declared-type parameter
(x) -> x+1 // Single inferred-type parameter
x -> x+1 // Parens optional for single inferred-type case
So the example has one method, and all it does is throw an exception.
() -> { throw new IllegalArgumentException("a message"); }
And in your code, you're actually defining one method with no parameters, calling your method with one parameter.
() -> { LocalizationUtils.getResources(null); }

Should exceptions be used to unwinding process back to main?

Now this is really quite difficult for me to explain so please bear with me.
I've been wondering as of late the best way to "unwind" every chained method back to a main method when certain circumstances are met. For example, say I make a call to a method from Main and from that method I call another one and so on. At some point I may want to cancel all further operations of every method that is chained and simply return to the Main method. What is the best way to do this?
I'll give a scenario:
In the following code there are 3 methods, however when Method1 calls Method2 with a null value it should unwind all the way back to Main without further operations in Method2 (EG the "Lots of other code" section).
public static void main(String[] args)
{
try
{
Method1();
}
catch( ReturnToMainException e )
{
// Handle return \\
}
}
public static void Method1() throws ReturnToMainException
{
String someString = null;
Method2( someString );
// Lots more code after here
}
public static boolean Method2( String someString )
{
if( someString == null )
throw new ReturnToMainException();
else if( someString.equals( "Correct" ))
return true;
else
return false;
}
In this example I use a throw which I've read should only be used in "Exceptional Circumstances". I often run into this issue and find myself simply doing If/Else statements to solve the issue, but when dealing with methods that can only return True/False I find I don't have enough options to return to decide on an action. I guess I could use Enumerators or classes but that seems somewhat cumbersome.
I use a throw which I've read should only be used in "Exceptional Circumstances". I often run into this issue and find myself simply doing If/Else statements to solve the issue
Exception throwing is relatively expensive so it should not be used without careful thought but I believe that your example is a ok example of proper usage.
In general, you should use exceptions only for "exceptional" behavior of the program. If someString can be null through some sort of user input, database values, or other normal mechanism then typically you should handle that case with normal return mechanisms if possible.
In your case, you could return a Boolean object (not a primitive) and return null if someString is null.
private static Boolean method2( String someString ) {
if (someString == null) {
return null;
}
...
}
Then you would handle the null appropriately in the caller maybe returning a boolean to main based on whether or not the method "worked".
private static boolean method1() {
...
Boolean result = method2(someString);
if (result == null) {
// the method didn't "work"
return false;
}
Then in main you can see if method1 "worked":
public static void main(String[] args) {
if (!method1()) {
// handle error
}
...
}
Notice that I downcased your method names and changed the permissions of your methods to private both which are good patterns.
Enumerators or classes but that seems somewhat cumbersome.
Yeah indeed. It depends a bit on how this code is used. If it is a API method that is called by others, you might want to return some sort of Result class which might provide feedback like a boolean that the argument was null. Or you might throw an IllegalArgumentException in that case. Instead, if this is an internal local private method, then I'd vote for a simpler way of handling argument errors. Either way I'd use javadocs to document the behavior so you don't trip up future you.
Hope this helps.

How to continue test after JUnit ExpectedException if thrown?

I have set up some JUnit (4.12) test with the ExpectedException feature, and I would like the test to continue after the expected exception. But I never see the log '3', as the execution seems to stop after the exception, event if catch?
Is this actually possible, and how?
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void testUserAlreadyExists() throws Exception {
log.info("1");
// Create some users
userService.createUser("toto1");
userService.createUser("toto2");
userService.createUser("toto3");
Assert.assertTrue( userService.userExists("toto1") );
Assert.assertTrue( userService.userExists("toto2") );
Assert.assertTrue( userService.userExists("toto3") );
log.info("2");
// Try to create an existing user
exception.expect(AlreadyExistsException.class);
userService.createUser("toto1");
log.info("3");
}
You cannot do that, when the exception is thrown it's thrown for real, ExpectedException rule or not.
If you really want this kind of behaviour, you can go back to the "old school" pattern:
try {
userService.createUser("toto1");
Assert.fail("expecting some AlreadyExistsException here")
} catch (AlreadyExistsException e) {
// ignore
}
log.info("3");
But I wouldn't bother for some log.
This SO solution seems to do what you want to do: JUnit continue to assert things after expected exception
I myself was thinking something similar. To continue with the test, you would have to catch the exception yourself in the test. This solution shows an elegant way of doing that.
Note: If you make a rule to expect an exception (as you did), the test will return successful as soon as that exception is thrown.
Reference: http://junit.org/javadoc/latest/org/junit/rules/ExpectedException.html
If you don't want to add a lot of similar test methods for something that has many options to throw the expected exception and want to verify that it actually throws on all of the desired cases within a single unit-test instead, I'd suggest this (not pretty maybe) helpful schema:
#Test
public void testThatSomethingExpectedlyFails() {
for (int i = 1; i <= 3; i++) {
try {
switch (i) {
case 1: // smth here throws the exception when configuration #1;
case 2: // smth here throws the exception when configuration #2;
case 3: // smth here throws the exception when configuration #3;
}
} catch (ExceptionThatIsExpected expected) {
continue;
} catch (Exception unexpected) {
/* the test must fail when an unexpected exception is thrown */
fail("The test has failed due to an unexpected exception: " + unexpected.getMessage()); // or just re-throw this exception
}
/* the test must fail when a case completes without the expected exception */
fail("No expected exception occurred at case " + i);
}
}
The one could also iterate items (and even execute functions) of some preliminarily prepared list instead of switch-case with hard-coded integers.
First of all your test doesn't test one thing. It tests "userExists" and "createUser" under different conditions a.k.a. different scenarios. This is called an AssertionRoulette. You wouldn't need a hack to continue to log "3", if you would write tests, that fail fo the right reason.
If the tests fail for the right reason, you can see the scenario why it fails without doing all the logging stuff. The Junit-Runner does the logging for you already.
#Test
public void testUserExists_UserCreatedUserNotExistent_expectTrue()
{
// Create some users
userService.createUser("toto1");
// Assert That user exists
Assert.assertTrue( userService.userExists("toto1") );
}
#Test
public void testCreateUser_UserAlreadyCreated_expectAlreadyExistsExceptionIsThrown()
{
// Create some users
userService.createUser("toto1");
// Try to create an existing user
exception.expect(AlreadyExistsException.class);
userService.createUser("toto1");
}

Difference between Throws in method signature and Throw Statements in Java

I am trying to make it clear of the difference between Throws in method signature and Throw Statements in Java.
Throws in method signature is as following:
public void aMethod() throws IOException{
FileReader f = new FileReader("notExist.txt");
}
Throw Statements is as following:
public void bMethod() {
throw new IOException();
}
From my understanding, a throws in method signature is a notification that the method may throw such an exception. throw statement is what actually throw a created object under according circumstances.
In that sense, throws in method signature should always appear if there exist a throw statement in the method.
However, the following code doesn't seem doing so. The code is from the library. My question is why it is happening? Am I understanding the concepts wrong?
This piece of code is a copy from java.util.linkedList. #author Josh Bloch
/**
* Returns the first element in this list.
*
* #return the first element in this list
* #throws NoSuchElementException if this list is empty
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
Update on the answer:
update 1 : is above code the same as the following?
// as far as I know, it is the same as without throws
public E getFirst() throws NoSuchElementException {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
update 2 : For checked exception. Do I need to have "throws" in the signature? Yes.
// has to throw checked exception otherwise compile error
public String abc() throws IOException{
throw new IOException();
}
You are pretty much right on. Except for one thing I'll mention in a bit.
throws is as much a part of the method API as the name and the parameters. Clients know if they call that method, they need to handle that exception--by simply throwing it also or by catching it and handling it (which may in fact entail the throwing of another exception wrapping the original). throws is addressed at compile time.
throw is the actual act of letting the runtime know something bad happened--that the exceptional condition we were worried about has in fact taken place. So it needs to be dealt with at runtime.
But you weren't quite right when you said, "Throws in method signature should always appear if there exist a throw statement in the method." That is often true but not always. I could also call another method that throws an exception within my method, and if I don't catch it, my method needs to throw it. In that case, there is no explicit throw of the same exception by me.
The final point is that you only need to declare an exception in throws when the exception is a checked exception--meaning it is from the other side of the Exception class hierarchy from RuntimeException. Common checked exceptions are IOException and SQLException. Checked exceptions must be listed in the throws part of the method signature if you don't handle them yourself. Anything subclassing RuntimeException--like NoSuchElementException in your example and also the hated NullPointerException--is an unchecked exception and doesn't have to be caught or thrown or anything.
Typically, you use checked exceptions for recoverable problems (where the client knows what can happen and can gracefully handle the problem and move on) and unchecked exceptions for catastrophic problems (like can't connect to the database).
If you can get past all the AOP stuff, this is a great discussion of how you use checked and unchecked exceptions effectively.
Vidya provided great answer to your questions.
The most important words are "The final point is that you only need to declare an exception in throws when the exception is a checked exception"
Just to show you an example code what does this mean. Imagine that we would like to use FileOutputStream in order to pass some data. The function would look like this:
public void saveSomeData() throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = out.read() != -1) {
in.write(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close in
if (in != null) {
in.close(); // <-- If something bad happens here it will cause runtime error!
}
// Close out
...
}
}
Now imagine, if you wouldn't provide throws IOException and something bad happens inside finally{} statement - it would cause an error.
RuntimeExceptions dont have to be handled in try-catch block so they dont have to be declared as thrown and NoSuchElementException is RuntimeException because it extends it.
throw attribute in method signature, like you correctly guessed, is a hint to the compiler that the method raises an exception that must be caught by the caller. This kind of exception, namely called checked exception is something that the caller MUST always catch or dispatch to its caller again. This is something at compiler level, the signature specifies which exception the method is able to throw: this enforces a try-catch or re-dispatch in the caller and a throw statement somewhere inside the method, is a constraint that the developer places to specify something about the method behavior.
On the other hand other exceptions, namely unchecked or runtime exceptions, (NoSucheElementException is one example) are exceptions which you are not forced to specify becuase they arise from different situations.
The conceptual difference is that checked exception are usually used to warn about exceptional situation which should be handled somehow (think about IOException) by the developer, while unchecked are real errors (like NullPointerException or like in your example NoSuchElementException)

What is the best practice for checking exceptions in JUnit?

I am trying my hand at writing test cases. From what I have read, my tests should fail from the start and I should strive to make tests pass. However, I find myself writing tests checking boundaries and the exceptions they should cause:
#Test(expected=NegativeArraySizeException.class)
public void testWorldMapIntInt() {
WorldMap w = new WorldMap(-1, -1);
}
#Test(expected=IndexOutOfBoundsException.class)
public void testGetnIntnInt() {
WorldMap w = new WorldMap(10,10);
Object o = w.get(-1, -1);
}
However, this test passes by default because Java will throw the exception anyway. Is there a better way to handle these kinds of expected exceptions, possibly a way that fails by default-- forcing me to strive to handle these cases?
I agree that the style you present is not so good. The problem is that it doesn't check where in the method the exception is thrown, so it's possible to get false negatives.
We usually write tests for exceptions like this:
public void testWorldMapIntInt() {
try {
WorldMap w = new WorldMap(-1, -1);
Assert.fail("should have thrown IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException e) {}
}
Expected behaviour for WorldMap is to throw an exception if (-1, -1) passed into it
Initially it doesn't do that, so your test will fail as it does not see expected exception.
You implement the code for WorldMap correctly, including throwing exception when (-1, -1) passed in.
You rerun your test, it passes.
Sound like good TDD to me!
That seems like a fair test to write. WorldMap is no standard Java class. Presumably it's your own class. Therefore the test wouldn't be passing if you hadn't already written some code. This test will force you to throw (or propagate) an appropriate exception from your class. That sounds like a good test to me, which you should write before implementing the behavior.
I personally look for mistakes like that in the WorldMap constructor and throw an IllegalArgumentException, that way you can provide a better error message, such as what the value passed in was and what the expected range is.
As for having that test fail by default, I cannot think of a reasonable way of doing that if you are going to have it actually do something (if you are writing the tests first then it should fail because the constructor won't have any code).
Agree with accepted answer, try-fail-catch idiom, although ugly and cluttering the test, is much better than #Test(expcted=...) as it might report false positives.
A while back I implemented very simple JUnit rule to deal with exception testing in both safe and readable manner:
public class DefaultFooServiceTest {
#UnderTest
private FooService fooService = new DefaultFooService();
#Rule
public ExceptionAssert exception = new ExceptionAssert();
#Test
public void shouldThrowNpeWhenNullName() throws Exception {
//given
String name = null;
//when
fooService.echo(name);
//then
exception.expect(NullPointerException.class);
}
#Test
public void shouldThrowIllegalArgumentWhenNameJohn() throws Exception {
//given
String name = "John";
//when
fooService.echo(name);
//then
exception.expect(IllegalArgumentException.class)
.expectMessage("Name: 'John' is not allowed");
}
}
See blog post and source.

Categories