Плохая форма для теста 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 или просто заниматься ими самими?
Спасибо за помощь.
Ответы
Ответ 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 лучше обработать исключение и отобразить соответствующий обмен сообщениями из теста, чем выбросить его из теста.