Подтвердить что ты не робот

Плохая форма для теста JUnit для исключения исключений?

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

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

@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 или просто заниматься ими самими?

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

4b9b3361

Ответ 1

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

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

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

  • Ваш assertTrue(addr.getOctets() == null); бесполезен.
  • Предназначение и синтаксис более ясные IMO и, следовательно, их легче читать.

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

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

Ницца, не так ли?

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

Ответ 2

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

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

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

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

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

Ответ 3

Поскольку 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, потому что правило "ломает порядок /act/assert [...] поток" Unit test (он предлагает использовать Catch Exception Библиотека). Хотя я могу понять его аргумент, я думаю, что использование правила прекрасное, если вы не хотите вводить другую стороннюю библиотеку (использование правила лучше, чем исключение "вручную" ) в любом случае.

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Важная вещь, которую следует помнить при тестировании, - это покрытие кода.

Ответ 6

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

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

Ответ 7

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
Тест должен проводиться на общедоступных методах/возможностях класса, утверждающих, что они работают как исключенные

Ответ 8

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