Плохая форма для теста JUnit, чтобы бросить исключение? - PullRequest
56 голосов
/ 03 декабря 2009

Я довольно новичок в JUnit, и я действительно не знаю, что такое лучшие практики для исключений и обработки исключений.

Например, допустим, я пишу тесты для класса IPAddress. Он имеет конструктор IPAddress (String addr), который будет генерировать исключение InvalidIPAddressException, если addr имеет значение null. Насколько я могу судить по поиску, тест для нулевого параметра будет выглядеть следующим образом.

@Test
public void testNullParameter()
{
    try
    {
        IPAddress addr = new IPAddress(null);
        assertTrue(addr.getOctets() == null);
    }
    catch(InvalidIPAddressException e)
    {
        return;
    }

    fail("InvalidIPAddressException not thrown.");
}

В этом случае try / catch имеет смысл, потому что я знаю, что исключение наступает.

Но теперь, если я хочу написать testValidIPAddress (), есть несколько способов сделать это:

Путь № 1:

@Test
public void testValidIPAddress() throws InvalidIPAddressException
{
    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();

    assertTrue(octets[0] == 127);
    assertTrue(octets[1] == 0);
    assertTrue(octets[2] == 0);
    assertTrue(octets[3] == 1);
}

Путь №2:

@Test
public void testValidIPAddress()
{
    try
    {
        IPAddress addr = new IPAddress("127.0.0.1");
        byte[] octets = addr.getOctets();

        assertTrue(octets[0] == 127);
        assertTrue(octets[1] == 0);
        assertTrue(octets[2] == 0);
        assertTrue(octets[3] == 1);
    }
    catch (InvalidIPAddressException e)
    {
        fail("InvalidIPAddressException: " + e.getMessage());
    }
}

Является ли стандартной практикой бросать неожиданные исключения в JUnit или просто разбираться с ними самостоятельно?

Спасибо за помощь.

Ответы [ 8 ]

91 голосов
/ 03 декабря 2009

На самом деле старый стиль тестирования исключений заключается в том, чтобы обернуть блок try вокруг кода, генерирующего исключение, а затем добавить оператор fail() в конце блока try. Примерно так:

public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}

Это не сильно отличается от того, что вы написали, но:

  1. Ваш assertTrue(addr.getOctets() == null); бесполезен.
  2. Цель и синтаксис более понятны для ИМО и поэтому легче читаются.

Тем не менее, это немного уродливо. Но здесь на помощь приходит JUnit 4, так как тестирование исключений является одним из самых больших улучшений в JUnit 4. С JUnit 4 вы можете теперь написать свой тест следующим образом:

@Test (expected=InvalidIPAddressException.class) 
public void testNullParameter() throws InvalidIPAddressException {
    IPAddress addr = new IPAddress(null);
}

Хорошо, не правда ли?

Теперь, что касается реального вопроса, если я не ожидаю, что будет сгенерировано исключение, я определенно выберу путь № 1 (потому что он менее многословен) и позволил бы JUnit обработать исключение и провалил тест, как ожидалось.

10 голосов
/ 03 декабря 2009

Для тестов, где я не ожидаю исключения, я не пытаюсь его поймать. Я позволил JUnit перехватить исключение (оно делает это надежно) и не учитывал его вообще, кроме объявления причины throws (если требуется).

Я отмечаю ре. ваш первый пример того, что вы не используете аннотацию @expected, а именно

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
  }

Я использую это для всех тестов, которые я тестирую для создания исключений. Это короче, чем эквивалентный шаблон catch / fail, который мне пришлось использовать с Junit3.

9 голосов
/ 26 марта 2013

Начиная с JUnit 4.7 у вас есть возможность использовать правило ExpectedException , и вы должны его использовать. Правило дает вам возможность точно определить вызываемый метод, в который должно быть добавлено исключение в вашем тестовом коде. Более того, вы можете легко сопоставить строку с сообщением об ошибке исключения. В вашем случае код выглядит так:

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

    @Test
    public void test() {
        //working code here...
        expectedException.expect(InvalidIPAddressException.class);
        IPAddress addr = new IPAddress(null);
    }

ОБНОВЛЕНИЕ: В своей книге Практическое модульное тестирование с JUnit и Mockito Томек Качановский выступает против использования ExpectedException, потому что правило "нарушает порядок / действие / утверждение [...] поток "модульного теста (он предлагает вместо этого использовать Catch Exception Library ). Хотя я могу понять его аргумент, я думаю, что использовать правило хорошо, если вы не хотите вводить другую стороннюю библиотеку (лучше использовать правило, чем ловить исключение «вручную» в любом случае).

0 голосов
/ 05 ноября 2013

Reg: тестирование на исключения
Я согласен с "Pascal Thivent", т.е. использовать @Test (expected=InvalidIPAddressException.class)


Reg: Тестирование на testValidIPAddress

IPAddress addr = new IPAddress("127.0.0.1");
byte[] octets = addr.getOctets();

Я бы написал тест вроде

class IPAddressTests
{

    [Test]
    public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect()
    {
         // Test code here
    }

}

Дело в том, что testinput является VALID ipAddress
Тест должен проводиться для открытых методов / возможностей класса, утверждая, что они работают как исключение

0 голосов
/ 30 ноября 2011

IMO, лучше обработать исключение и показать соответствующий обмен сообщениями из теста, чем выбросить его из теста.

0 голосов
/ 03 декабря 2009

В общем, путь № 1 - это путь, нет причины вызывать сбой из-за ошибки - в любом случае тест по существу не удался.

Единственный способ времени №2 имеет смысл, если вам нужно хорошее сообщение о том, что пошло не так, и только исключение не даст вам этого. В этом случае имеет смысл лучше определить причину сбоя.

0 голосов
/ 03 декабря 2009

если я понимаю ваш вопрос, ответ либо - личное предпочтение.

лично я бросаю свои исключения в тестах. по моему мнению, тест, не пройденный по утверждению, эквивалентен тесту, не прошедшему по необработанному исключению. оба показывают что-то, что нужно исправить.

при тестировании важно помнить о покрытии кода.

0 голосов
/ 03 декабря 2009

Для нулевого теста вы можете просто сделать это:

public void testNullParameter() {
    try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
    }
    catch(InvalidIPAddressException e) { }
}

Если в исключении есть сообщение, вы также можете проверить это сообщение в улове, если хотите. Э.Г.

String actual = e.getMessage();
assertEquals("Null is not a valid IP Address", actual);

Для действительного теста вам не нужно перехватывать исключение. Тест автоматически провалится, если исключение выдается и не перехватывается. Таким образом, путь № 1 - это все, что вам нужно, поскольку он потерпит неудачу, и трассировка стека будет в любом случае доступна для вашего удовольствия от просмотра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...