2009-10-20 2 views
17

При тестировании исключений с помощью PHPUnit, каков наилучший способ потребовать, чтобы каждый оператор или утверждение должны вызывать исключение для прохождения теста?Как проверить несколько исключений с помощью PHPUnit?

Я в принципе хочу сделать что-то вроде этого:

public function testExceptions() 
{ 

    $this->setExpectedException('Exception'); 

    foo(-1); //throws exception 
    foo(1); //does not throw exception 

} 

//Test will fail because foo(1) did not throw an exception 

Я придумал следующий, который делает работу, но довольно уродливые ИМО.

public function testExceptions() 
{ 

    try { 
     foo(-1); 
    } catch (Exception $e) { 
     $hit = true; 
    } 

    if (!isset($hit)) 
     $this->fail('No exception thrown'); 

    unset($hit); 

    try { 
     foo(1); 
    } catch (Exception $e) { 
     $hit = true; 
    } 

    if (!isset($hit)) 
     $this->fail('No exception thrown'); 

    unset($hit); 

} 

ответ

16

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

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

/** 
* @expectedException Exception 
*/ 
public function testBadFooThrowsException() 
{ 
    // optional, can also do it from the '@expectedException x' 
    //$this->setExpectedException('Exception'); 
    foo(-1); //throws exception -- good. 
} 

public function testFooDoesNotThrowException() 
{ 
    foo(1); //does not throw exception 
} 
+1

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

+3

Вы также можете использовать аннотацию @dataProvider для передачи значений (и даже имя ожидаемого исключения - используйте это с помощью $ this-> setExpectedException ($ x) '). Добавление нового тестового значения (которое создало бы исключение) было бы просто другой записью массива в функции dataProvider. –

+1

Роберт Мартин сказал, что всегда разбивает дела на разные тесты. На мой взгляд, ответ @AlisterBulman показывает идеальное решение проблемы. –

1

Это не имеет смысла для меня.

Я думаю, вы пытаетесь протестировать несколько отдельных вещей с помощью одного тестового примера, который является плохим.

Когда foo() выбрасывает ожидаемое исключение, тестовый пример успешно и bar() не запускается.

Просто создайте два отдельных тестовых примера, которые намного меньше кода, чем то, что вы произвели во втором листинге.

Или объясните, почему было бы целесообразно запустить bar(), после того, как foo() потерпел неудачу с исключением, когда он тоже выдаст исключение.

+0

Я пытался проверить вызов одной и той же функции различными аргументами (и я отредактировал свой пример, чтобы лучше отразить это). Пример в руководстве PHPUnit показывает несколько связанных утверждений, сделанных в одном тесте, поэтому я пытался реплицировать это, но с исключениями. – etheros

6

Слегка чистый код (но я бы еще предложить разделив тесты:

try { 
    foo(-1); 
    $this->fail('No exception thrown'); 
} catch (Exception $e) {} 
0

Расширение на @ dave1010 отвечают, вот как я решил эту проблему Это позволяет сохранить все эти ". утверждений «аккуратно и аккуратно в течение одного теста». Вы просто определяете массив переменных, которые должны сбой теста, а затем прокручивать каждый из них и видеть, возникает ли исключение. Если какой-либо сбой (без исключения), тест терпит неудачу, иначе испытания проводятся.

<?php 

public function testSetInvalidVariableType() 
{ 
    $invalid_vars = array(
     '',     // Strings 
     array(),   // Arrays 
     true,    // Booleans 
     1,     // Integers 
     new \StdClass  // Objects 
    ); 

    foreach ($invalid_vars as $var) { 
     try { 
      $object->method($var); 
      $this->fail('No exception thrown for variable type "' . gettype($var) . '".'); 
     } catch (\Exception $expected) { 
     } 
    } 
} 
+0

'fail' throws' PHPUnit_Framework_AssertionFailedError'. Вы должны использовать пользовательские исключения в своем коде. – sectus

15

Я думаю, что это очень распространенная ситуация в модульном тестировании. Подход, который я использую в этом случае, использует phpunit dataProviders. Все работает так, как ожидалось, и тестовый код становится более четким и кратким.

class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function badValues() 
    { 
     return array(
      array(-1), 
      array(1) 
     ); 
    } 


    /** 
    * @dataProvider badValues 
    * @expectedException Exception 
    */ 
    public function testFoo($badValue) 
    { 
     foo($badValue); 
    } 
} 
+2

Очень хорошая идея ... испытайте множество исключений с одним тестом. – Andrew

+0

Должен ли 'array (1)' быть там? –

+3

это решение является самым красивым и элегантным и должно быть принятым ответом. –