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

Как проверить несколько исключений с помощью 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);

}
4b9b3361

Ответ 1

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

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

/**
 * @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
}

Ответ 2

Я думаю, что это очень распространенная ситуация в модульном тестировании. Подход, который я использую в этом случае, использует 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);
    }
}

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Ответ 5

Развернувшись на @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) {
        }
    }
}