Java 我可以在一个测试方法中测试多个抛出的异常吗?

Java 我可以在一个测试方法中测试多个抛出的异常吗?,java,testing,exception-handling,junit,junit4,Java,Testing,Exception Handling,Junit,Junit4,我有一个很好的指定接口,并以此为基础编写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 c

我有一个很好的指定接口,并以此为基础编写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);
    }

[...]

}
公共抽象类AbstractShortMessageServiceTest
{
字符串消息;
字符串用户名;
字符串主题;
/**
*@返回ShortMessageService实现的新空实例。
*/
受保护的抽象ShortMessageService getNewShortMessageService();
私人短信服务;
@统治
抛出公共ExpectedException=ExpectedException.none();
@以前
public void setUp()引发异常
{
messageService=getNewShortMessageService();
message=“测试消息”;
username=“TestUser”;
topic=“TestTopic”;
}
@试验
public void testCreateMessage()
{
assertEquals(新长(1L),messageService.createMessage(用户名,消息,主题));
}
@测试(预期=IllegalArgumentException.class)
public void testCreateMessageUserMissing()引发异常
{
messageService.createMessage(“,消息,主题);
}
@测试(预期=IllegalArgumentException.class)
public void testCreateMessageTopicMissing()引发异常
{
messageService.createMessage(用户名,message,“”);
}
@测试(预期=IllegalArgumentException.class)
public void TestCreateMessageToolLong()引发异常
{
字符串消息=”;

对于(inti=0;i来说,将它们组合在一个方法中可能不是最好的主意,因为您不知道哪个测试用例抛出了哪个异常

例如,如果你有这条线

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

    // ...
}

不幸的是,@Test注释不允许捕获多个异常类型(api引用)

作为第一种选择,我主张迁移到TestNG。如果您的团队不允许这样做,那么在JUnit中您可以做的事情很少

一定要使用参数化测试用例,这样就不必为每个测试用例()编写一个测试函数

  • 按异常类型对测试数据进行分组

    @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) {}
    
  • 结合方法签名中的异常类型。(这是我推荐的)下面的大致轮廓

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

  • 这很好,但您可以使用ExpectedException规则对此进行改进:
    @Test (expected = Exception.class)
    public void testException(String username, String message, String topic) {}