Phpunit, как проверить, если метод "ничего"?

class Testme()
{
    public function testMe ($a)
    {
        if ($a == 1)
        {
            throw new Exception ('YAY');
        }
    }
}

поэтому его легко проверить, если он исключил исключение

/**
 * @expectedException Exception
 */
public function test()
{
    new Testme(1);
}

но что, если ничего не сделал?

public function test()
{
    new Testme(2);
 ?? ? ? ? ?
}

Ответ 1

Сценарии

У вас есть два возможных сценария, когда функция ничего не делает:

Сценарий 1: нет возврата

Ваша функция ничего не делает, потому что вы не выполняете в ней действия и не включаете в нее ключевое слово return:

public function doNothing()
{
    // Do nothing.
}

Сценарий 2: с выражением о возврате

Ваша функция ничего не делает, потому что вы не выполняете в ней никаких действий, и вы включаете в нее ключевое слово return без выражения какого-либо возвращаемого значения:

public function doNothing()
{
    // Do nothing.
    return;
}

Другие сценарии

Я оставлю из случаев для рассмотрения следующих сценариев:

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

  2. В случае, если вы ничего не делаете, но возвращаете что-то, вам следует выполнить модульное тестирование возвращаемого значения.

Изучение документации в руководстве по PHP

Для первого случая руководство PHP документирует, что вычисленное выражение функции будет null. Здесь написано: http://php.net/manual/en/functions.returning-values.php в примечании:

Если возвращение опущено, будет возвращено значение NULL.

Во втором случае руководство по PHP документирует, что вычисленное выражение функции также будет null. Здесь написано: http://php.net/manual/en/function.return.php в примечании:

Если параметр не указан, то круглые скобки должны быть опущены и будет возвращено значение NULL. [...]

Заключение

Поэтому ясно документировано, что функция, которая "ничего не делает", обязательно оценивается как null.

Как проверить функцию, которая ничего не делает

Просто подтвердите свои ожидания:

$this->assertNull( $sut->doNothing() );

Таким образом, вы "выполняете" свою функцию, выполняете ее, делая покрытие кода завершением всех строк, и "ожидаете", что "ничего не произошло", проверяя null значение его оценки в качестве выражения, как описано в документации.

Как проверить конструктор, который ничего не делает

Тем не менее, чтобы проверить конструктор... ну... здравый смысл: какова цель конструктора? Создать объект (экземпляр) определенного типа (класса), верно?

Итак... Я предпочитаю начинать 100% моих модульных тестов, проверяя, что $sut был создан. Это ОЧЕНЬ первый тест, который я пишу, когда создаю новый объект этого класса. Это тест, который я пишу еще до того, как класс существует. В конце концов, для этого и нужен конструктор. Красная полоса. Затем я создаю класс. Зеленая полоса.

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

Я обычно делал бы что-то вроде этого:

//-------------------------------------------------//
// Tests                                           //
//-------------------------------------------------//

/** @dataProvider validEmailProvider **/
public function testCreationIsOfProperClass( string $email )
{
    $sut = $this->getSut( $validEmail );
    $this->assertInstanceOf( Email::class, $sut );
}

/** @dataProvider invalidEmailProvider **/
public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
{
    $this->expectException( EmailException::class );
    $this->getSut( $invalidEmail );
}

//-------------------------------------------------//
// Data providers                                  //
//-------------------------------------------------//

public function validEmailProvider() : array
{
    return
    [
        [ '[email protected]' ],
        [ '[email protected]ylongTLD' ],
    ]
}

public function invalidEmailProvider() : array
{
    return
    [
        [ 'missing_at_symbol' ],
        [ '[email protected]' ],
    ]
}

//-------------------------------------------------//
// Sut creators                                    //
//-------------------------------------------------//

private function getSut( string $email ) : Email
{
    return new Email( $email );
}

Поскольку я использую PHP 7.0 и помещаю типы везде, как при вводе параметров, так и в возвращаемых типах, если созданный объект не был Email, функция getSut() сначала потерпит неудачу.

Но даже если я напишу его, опуская тип возвращаемого значения, тест проверяет ожидаемое: new Email( '[email protected]' ); само по себе является выражением, которое должно оцениваться как "что-то" класса Email::class.

Как проверить конструктор, который что-то делает

Код запаха. Конструктор, вероятно, не должен делать работу. Если есть, просто сохраните параметры. Если конструктор "работает", кроме хранения параметров, рассмотрите ленивую обработку на геттерах или делегирование этой работы на фабрике или около того.

Как проверить конструктор, который "ничего не делает, кроме сохранения параметров"

Так же, как до +, затем получить данные.

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

Надеюсь, что это поможет.

Ответ 2

Это невозможно. Добавьте оператор return и верните результат.

class Testme()
{
    public function testMe ($a)
    {
        if ($a == 1)
        {
            throw new Exception ('YAY');
        }

        return true;
    }
}

а затем

$object = new Testme();
$this->assertTrue($object->testMe(2));

Ответ 3

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

В соответствии с этот поток проблем, нет встроенного решения для тестирования чего-то вроде DoesNotThrowException в PHPUnit (пока).

Итак, да, одним из решений было бы вернуть некоторое фиктивное значение из вашего метода, например

public function testMe ($a)
{
    if ($a == 1) { throw new Exception ('YAY'); }

    return true;
}

а затем утвердите его в своем тесте. Но если вы не хотите изменять код только для теста, вы можете обойти его:

public function testExceptionIsNotThrown()
{
    try {
        new Testme(2);
    }
    catch(Exception $e) {
        /* An exception was thrown unexpectedly, so fail the test */
        $this->fail();
    }

    /* No exception was thrown, so just make a dummy assertion to pass the test */
    $this->assertTrue(true);
}

Это может показаться взломанным и не очень интуитивным, но если он глуп, но он работает, это не глупо.

Ответ 4

Я наткнулся на ту же проблему. Чтобы убедиться, что "ничего" не произошло, достаточно просто вызвать метод в вашем unit test. Если он терпит неудачу, тест все равно потерпит неудачу.

Если вы просто вызываете свой метод без аннотации @expectedException, подобной этой

public function test()
{
    new Testme(1);
}

вы получите сообщение об ошибке

There was 1 error:

1) Testme::testMe
Exception: YAY

Ответ 5

2018+

На сегодняшний день лучшей практикой является аннотация именно для этих случаев:

/**
 * @doesNotPerformAssertions
 */
public function testSomething()
{
    $someService = new SomeObject();
    $someService->shallNotFail();
}