简体   繁体   中英

JAVA: JUnitTest, testing method that throws two exceptions

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.
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM