Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check that a method throws an exception when applied to any element of a list of values

I want to write unit tests for a parser and would like to check that it correctly throws an exception for all input strings in a list. Now as I understand it, the standard approach with JUnit would be to write a separate test method for each case:

public final class ParseFailureTest1 {
    @Test(expected = ParseException.class)
    public void testParseFailure1() throws Exception {
        Parser.parse("[1 2]"); // Missing comma
    }

    @Test(expected = ParseException.class)
    public void testParseFailure2() throws Exception {
        Parser.parse("[1, 2,]"); // Additional commas
    }
}

But as I want to apply the same test to 20 or 50 different strings, it seems impractical.

An alternative would be to explicitly check for an exception with a catch block:

public final class ParseFailureTest2 {
    @Test
    public void testParseFailure() throws Exception {
        List<String> documents = Arrays.asList(
            "[1 2]", // Missing comma
            "[1, 2,]"); // Additional commas

        for (String document : documents) {
            try {
                Parser.parse(document);

                throw new AssertionError("Exception was not thrown");
            } catch (ParseException e) {
                // Expected, do nothing.
            }
        }
    }
}

But this is error prone and I won't get any information about which exception was expected and if a different exception was thrown, it would count as a test error and not a failure.

My solution would be to use a method similar to expectException below:

public final class ParseFailureTest3 {
    @Test
    public void testParseFailure() throws Exception {
        List<String> documents = Arrays.asList(
            "[1 2]", // Missing comma
            "[1, 2,]"); // Additional commas

        for (final String document : documents) {
            expectException(ParseException.class, new TestRunnable() {
                @Override
                public void run() throws Throwable {
                    Parser.parse(document);
                }
            });
        }
    }

    public static void expectException(Class<? extends Throwable> expected, TestRunnable test) {
        try {
            test.run();
        } catch (Throwable e) {
            if (e.getClass() == expected) {
                return; // Expected, do nothing.
            } else {
                throw new AssertionError(String.format("Wrong exception was thrown: %s instead of %s", e.getClass(), expected), e);
            }
        }

        throw new AssertionError(String.format("Expected exception was not thrown: %s", expected));
    }

    public interface TestRunnable {
        void run() throws Throwable;
    }
}

Is there a method for that purpose in the JUnit framework or a related library or would you suggest a different approach (or one of my rejected approaches) to the problem?

like image 773
Feuermurmel Avatar asked Dec 08 '13 12:12

Feuermurmel


2 Answers

Use JUnit4 for Parameterized test feature. The following code should work.

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(value = Parameterized.class)
public class ParseTest {

    private String parseValue;

    public ParseTest(String parseValue) {
        this.parseValue = parseValue;
    }

    @Parameters
    public static Collection<Object[]> data() {
        Object[][] data = new Object[][] { { "[1 2]" }, { "[1,2,]" } };
        return Arrays.asList(data);
    }

    @Test(expected = ParseException.class)
    public void testParseFailure1() throws Exception {
        Parse.parse(parseValue);
    }

}

For more info refer http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/

like image 159
maheeka Avatar answered Nov 14 '22 07:11

maheeka


Use the fail() method:

@Test
public void testParseFailure() throws Exception {
    List<String> documents = Arrays.asList(
        "[1 2]", // Missing comma
        "[1, 2,]"); // Additional commas

    for (String document : documents) {
        try {
            Parser.parse(document);
            fail("Parsing " + document + " should have thrown a ParseException");
        } 
        catch (ParseException e) {
            // Expected, do nothing.
        }
    }
}
like image 21
JB Nizet Avatar answered Nov 14 '22 07:11

JB Nizet