【问题标题】:Can I test for multiple thrown exceptions in one test method?我可以在一种测试方法中测试多个抛出的异常吗?
【发布时间】:2014-01-03 09:05:48
【问题描述】:

我有一个明确指定的接口,我为此编写了我的 JUnit 测试:

public interface ShortMessageService {

     /**
     * Creates a message. A message is related to a topic
     * Creates a date for the message
     * @throws IllegalArgumentException, if the message is longer then 255 characters.
     * @throws IllegalArgumentException, if the message ist shorter then 10 characters.
     * @throws IllegalArgumentException, if the user doesn't exist
     * @throws IllegalArgumentException, if the topic doesn't exist
     * @throws NullPointerException, if one argument is null.
     * @param userName
     * @param message
     * @return ID of the new created message
     */
     Long createMessage(String userName, String message, String topic);

[...]

}

如您所见,实现可能会引发各种异常,我必须为此编写测试。我目前的方法是为接口中指定的一种可能的异常编写一种测试方法,如下所示:

public abstract class AbstractShortMessageServiceTest
{

    String message;
    String username;
    String topic;

    /**
     * @return A new empty instance of an implementation of ShortMessageService.
     */
    protected abstract ShortMessageService getNewShortMessageService();

    private ShortMessageService messageService;

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

    @Before
    public void setUp() throws Exception
    {
        messageService = getNewShortMessageService();
        message = "Test Message";
        username = "TestUser";
        topic = "TestTopic";
    }

    @Test
    public void testCreateMessage()
    {
        assertEquals(new Long(1L), messageService.createMessage(username, message, topic));
    }

    @Test (expected = IllegalArgumentException.class)
    public void testCreateMessageUserMissing() throws Exception
    {
        messageService.createMessage("", message, topic);
    }

    @Test (expected = IllegalArgumentException.class)
    public void testCreateMessageTopicMissing() throws Exception
    {
        messageService.createMessage(username, message, "");
    }

    @Test (expected = IllegalArgumentException.class)
    public void testCreateMessageTooLong() throws Exception
    {
        String message = "";
        for (int i=0; i<255; i++) {
            message += "a";
        }
        messageService.createMessage(username, message, topic);
    }


    @Test (expected = IllegalArgumentException.class)
    public void testCreateMessageTooShort() throws Exception
    {
        messageService.createMessage(username, "", topic);
    }

    @Test (expected = NullPointerException.class)
    public void testCreateMessageNull() throws Exception
    {
        messageService.createMessage(username, null, topic);
    }

[...]

}

所以现在我必须为接口中定义的那个方法定义很多测试方法,这感觉很尴尬。我可以将所有这些异常测试组合到一种测试方法中吗?或者最佳做法是什么?

【问题讨论】:

    标签: java testing exception-handling junit junit4


    【解决方案1】:

    将它们全部组合在一个方法中可能不是最好的主意,因为您不会真正知道哪个测试用例引发了哪个异常。

    例如,如果你有这条线

    messageService.createMessage(username, null, topic);
    

    应该抛出一个NullPointerException,但它却抛出了一个IllegalArgumentException,你不希望这算作成功。

    如果您想在一个测试用例中测试该方法的所有异常,那么一个不错的选择是将每个异常测试包装在 try..catch 块中。

    例如,你可以有

    @Test
    public void testCreateMessageExceptions() {
        // test #1: a null message
        try {
            messageService.createMessage(username, null, topic);
            // if it got this far, that's a problem!
            fail();
        } catch(NullPointerException e) {
            // great, that's what it's meant to do! continue testing
        } catch(Exception e) {
            // if it threw the wrong type of exception, that's a problem!
            fail();
        }
    
        // test #2: an empty user
        try {
            messageService.createMessage("", message, topic);
            fail();
        } catch(IllegalArgumentException e) {
    
        } catch(Exception e) {
            fail();
        }
    
        // ...
    }
    

    【讨论】:

      【解决方案2】:

      很遗憾,@Test 注解不允许捕获多种异常类型(api 参考 http://junit.sourceforge.net/javadoc/org/junit/Test.html)。

      作为第一个选项,我会提倡迁移到 TestNG。如果您的团队不允许这样做,您可以在 JUnit 中做一些事情。

      一定要使用参数化测试用例,这样您就不必为每个测试用例编写一个测试函数 (http://junit.sourceforge.net/javadoc/org/junit/runners/Parameterized.html)。从这里开始,有几个选项。

      1. 按异常类型对测试数据进行分组。

        @Test (expected = IllegalArgumentException.class)
        public void testIllegalArgumentException(String username, String message, String topic) {}
        
        @Test (expected = NullPointerException.class)
        public void testNullPointerException(String username, String message, String topic) {}
        
      2. 在您的方法签名中组合异常类型。 (这是我推荐的)下面的粗略轮廓......

        public void testException(String username, String message, String topic, Class<? extends Exception>[] expectedExceptionClasses) {
            try {
                // exception throwing code
            } catch (Exception e) {
                boolean found = false;
                for (Class<?> expectedException : expectedExceptions) {
                    if (e instanceof expectedException) {
                        found = true;
                    }
                }
                if (found) {
                    return;
                }
            }
            Assert.fail();
        }
        
      3. 将所有测试放在 Exception 类下(我感觉你不想这样做。)。

        @Test (expected = Exception.class)
        public void testException(String username, String message, String topic) {}
        

      【讨论】:

      猜你喜欢
      • 2014-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 1970-01-01
      • 2018-05-19
      • 2023-01-25
      相关资源
      最近更新 更多