【问题标题】:Check that a method throws an exception when applied to any element of a list of values检查方法在应用于值列表的任何元素时是否引发异常
【发布时间】:2013-12-08 12:34:40
【问题描述】:

我想为解析器编写单元测试,并想检查它是否正确地为列表中的所有输入字符串抛出异常。现在据我了解,JUnit 的标准方法是为每种情况编写单独的测试方法:

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

但由于我想对 20 或 50 个不同的字符串应用相同的测试,这似乎不切实际。

另一种方法是使用catch 块显式检查异常:

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

但这很容易出错,我不会得到任何关于预期哪个异常的信息,如果抛出了不同的异常,它将被视为测试错误而不是失败。

我的解决方案是使用类似于下面expectException 的方法:

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

在 JUnit 框架或相关库中是否有用于此目的的方法,或者您会建议一种不同的方法(或我拒绝的方法之一)来解决这个问题?

【问题讨论】:

  • 你试过参数化JUnit测试吗?
  • @maheeka 这似乎是我想要的。你能写一个简短的例子并将其作为答案发布吗?
  • 如果您觉得有用,请接受答案。 :)
  • JUnit 参数化测试是要走的路。您不需要多个线程或任何类似的东西。 JUnit4 可以为您处理。 (参考下面我的回答)

标签: java exception junit


【解决方案1】:

使用 JUnit4 进行参数化测试功能。以下代码应该可以工作。

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);
    }

}

更多信息请参考http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/

【讨论】:

    【解决方案2】:

    使用fail() 方法:

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

    【讨论】:

    • 为了有点偏执,你还需要在捕获 ParseException 后捕获 Exception,以确保它不是 NPE。
    • 没有。这将被 JUnit 捕获,这将标记测试错误。
    • @JBNizet 感谢您的回答,但您没有抓住重点。我不想将该 try-catch 构造复制粘贴到我需要遍历列表的每个测试用例中。
    • 那么你的方法很好,除了:1.它应该使用fail(),2.它不应该对异常类进行相等检查,而是isInstanceof检查,以允许ParseException的子类被抛出。我不确定必须对整个输入列表进行相同的异常检查是一个如此频繁的要求,而且我不确定实现一个匿名类是否真的比简单的 try/catch 块更具可读性和更快。跨度>
    • @JBNizet 我同意你的两个反对意见。我已经检查过了,org.junit.Test 也接受了声明的预期异常的子类。
    【解决方案3】:

    这是一个替代方案,保留上一个答案中的 fail 想法,因为如果您关心在异常中获得正确的消息。

    public final class ParseFailureTest {
      @Test
      public void testParseFailure() throws Exception {
          Map<String, String> documents = new LinkedHashMap<String,String>();
          documents.put("[1 2]", "Missing comma");
          documents.put("[1, 2,]", "Additional commas");
    
          for (Entry<String,String> entry : documents.entrySet()) {
              try {
                  Parser.parse(entry.getKey());
                  fail("Parsing " + entry.getKey() + 
                          " should have thrown a ParseException");
              } catch (ParseException e) {
                  assertEquals(entry.getValue(), e.getMessage());
              }
          }
      }
    }
    

    【讨论】:

    • 我不推荐这个测试,因为它测试了两件事。
    • 嗯。我认为它是“解析器是否抛出了正确的异常?”。
    • 但这是一个不同的测试。
    猜你喜欢
    • 2022-12-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2014-07-10
    • 2022-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多