简体   繁体   中英

How can I write exception test for a Java program in JUnit?

package BasicTesting;

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Factorial_of_number {
    public static int factorial(int n)//factorial method
    {
        if (n == 0) {
            return 1;
        } else if (n < 1) {
            return -1;
        } else {
            return n * factorial(n - 1);
        }
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("Enter number to factorize ");
        int n = input.nextInt();
        System.out.println(factorial(n));
    }
}

Here, when we give negative numbers as input, they are not handled well. Actually, the program should return that we can't find factorial for negative numbers. By using this program, how can we write JUnit test cases to check whether the program give correct output for negative numbers? They are not checking negative numbers, so the system must fail while we test for exceptions, right? I am attaching my JUnit test case also.

package BasicTesting;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)

public class FactorialTest {

    public FactorialTest(int input, int expected) {
        super();
        this.input = input;
        this.expected = expected;
    }

    Factorial_of_number ob = new Factorial_of_number();
    private int input;
    private int expected;

    @Parameters
    public static Iterable<Object[]> testConditions() {
        return Arrays.asList(new Object[][]{{0, 1}, {1, 1},
                {5, 120}, {-3, -1}, {10, 3628800}, {12, 479001600}});
    }

    @Test
    public void factorial_test() {
        assertEquals(expected, Factorial_of_number.factorial(input));
    }
}

How can I check for that exception? Should I change my source program too? Kindly provide the edited code also.

First of all, you will have throw exception from your factorial method for negative values as you are returning -1 currently so your method will look like this -

    public static int factorial(int n)//factorial method
{
    if (n == 0) {
        return 1;
    } else if (n < 1) {
        throw new RuntimeException("Negative value, can not get factorial");
    } else {
        return n * factorial(n - 1);
    }
}

Then you can have your test cases like this -

@RunWith(Enclosed.class)
public class FactorialTest {
@RunWith(Parameterized.class)
public static class GoodTest {
    @Parameters
    public static Iterable<Object[]> goodTestConditions() {
        return Arrays.asList(new Object[][]{{0, 1}, {1, 1},
                {5, 120}, {10, 3628800}, {12, 479001600}});
    }


    public GoodTest(int input, int expected) {
        this.input = input;
        this.expected = expected;
    }

    private final int input;
    private final int expected;


    @Test
    public void factorial_test_good() {
        assertEquals(expected, Factorial_of_number.factorial(input));
    }
}

@RunWith(Parameterized.class)
public static class BadTest {
    @Parameters
    public static Iterable<Object[]> badTestConditions() {
        return Arrays.asList(new Object[][]{{-1}, {-10}});
    }


    public BadTest(int input) {
        this.input = input;
    }

    private final int input;


    @Test(expected = RuntimeException.class)
    public void factorial_test_good() {
        Factorial_of_number.factorial(input);
    }
}
}

First of all, you have to declare when and what type of exception is thrown in your testing method.

Then, for JUnit4 there are 3 main ways to assert an exception is thrown.

1) very primitive and non-elegant way:

It's basic way but still usable.

@Test    
public void factorial_test_good() {
   try {
       Factorial_of_number.factorial(input);
   } catch(Exception e) {
       Assert.assertThat(e, Matchers.<Exception>instanceOf(YourException.class));
   }
}

2) fast and easy to read:

Best if you want to identify failed test, but don't need details.

@Test(expected = YourException.class)
public void factorial_test_good() {
    Factorial_of_number.factorial(input);
}

3) most complex, best for detailed testing

Use this if you want to test specific scenario, like exception message or cause. There can be a method which throws multiple exceptions with different types or messages - then this is the best choice.

Initialize before test annotation:

@Rule
public ExpectedException exceptionRule = ExpectedException.none();

Test itself:

@Test    
public void factorial_test_good() {
   exceptionRule.expect(YourException.class);
   /* 
    * OPTIONAL
    * exceptionRule.expectMessage("Your custom exception message");
    * exceptionRule.expectCause(is(instanceOf(IllegalStateException.class)));
    */
   Factorial_of_number.factorial(input);
}

I guess this is far beyond your question, but it can be handy in future.

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