Юнит тестирование в PHP с помощью PHPUnit. Вникаем в assert-методы.

14-02-16 Php, Разное PHPUnit, Tests 1

В предыдущей статье, мы рассмотрели основы юнит тестирования с помощью PHPUnit, рассмотрели какие преимущества дает вам тестирование вашего кода. В этот раз мы окунемся немного глубже и рассмотрим этот фреймворк подробнее.

В PHPUnit есть два метода, которые, при правильном использовании, могут сделать тестирование немного проще. Два метода, markTestSkipped и markTestIncomplete, позволяют получать результаты тестирования отличные от обычных: прошел/не прошел. Метод markTestSkipped будет полезен, например если в ходе выполнения теста произойдет ошибка.

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

<?php

public function testThisMightHaveADb()
{
  $myObject->createObject();

  try {
    $db = new Database();
    $this->assertTrue($db->rowExists());
  } catch (DatabseException $e) {
    $this->markTestSkipped('This test was skipped because there was a database problem');
  }
}
?>

В этом примере возможно три варианта исхода событий. Все хорошо и тест завершился успешно. Тест assertTrue может провалиться, если метод rowExists вернет false.

И наконец, объект для связи с бд может возбудить исключение DatabaseException. Блок try/catch поймает исключение, что будет означать, что база недоступна и выполнение теста невозможно. Но вместо получения сообщения о неуспешном тестировании, мы просто его пропустим.  Результат “skipped” будет показан среди остальных и вы будете знать, что следует вернуться к этому тесту и решить проблему.

Метод markTestIncomplete предоставляет похожий функционал. Он дает возможность пометить тест как незаконченный. Это значит, что вы начали писать тест, но не закончили и намерены вернуться к нему позже. Или, например, для TDD (test driven development или разработка через тестирование) возможна ситуация, когда тест написан для кода, которого еще нет и тогда можно пометить этот тест как незавершенный. Вы даже можете указать сообщение с указанием того почему тест пропущен:

<?php

public function testAreNotEnoughHours()
{
  $this->markTestIncomplete("There aren't enough hours in the day to have my tests go green");
  $trueVariable = true;
  $this->assertTrue($trueVariable);
}
?>

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

Assertions, Assertions, Assertions

В прошлой статье мы рассмотрели некоторые базовые assert-методы в PHPUnit. Теперь мы рассмотрим примеры их использования.

Напишем класс, который будем тестировать:

<?php

class Testable
{
  public $trueProperty = true;
  public $resetMe = true;

  public $testArray = array(
    'first key' => 1,
    'second key' => 2
  );

  private $testString = "I do love me some strings";

  public function __construct()
  {
  }

  public function addValues($valueOne,$valueTwo) {
    return $valueOne+$valueTwo;
  }

  public function getTestString()
  {
    return $this->testString;
  }
}

?>

Напишем также тестирующий класс, в котором будут содержаться все тестирующие методы:

<?php

class TestableTest extends PHPUnit_Framework_TestCase
{
  private $_testable = null;

  public function setUp()
  {
    $this->_testable = new Testable();
  }

  public function tearDown()
  {
    $this->_testable = null;
  }

  /** здесь будут тестирующие методы */
}

?>

Мы используем методы setUp и tearDown для подготовки тестов. Метод setUp выполняется перед запуском всех тестов, а tearDown — после того как все тесты отработали. Поэтому в функции setUp мы создаем объект класса Testable, к которому будем иметь доступ во всех тестах.

True или False

Теперь, когда у нас есть инстанс класса для тестирования, можно начать его тестирование 🙂 Помните, что модульное тестирование  — это тестирование небольших модулей вашего кода, а не всего приложения сразу. Конечно, есть исключения, но нужно стараться делать небольшие тесты. Начнем с простейших проверок — assertTrue и assertFalse.

<?php

public function testTruePropertyIsTrue()
{
  $this->assertTrue($this->_testable->trueProperty,"trueProperty isn't true");
}

public function testTruePropertyIsFalse()
{
  $this->assertFalse($this->_testable->trueProperty, "trueProperty isn't false");
}

?>

Мы рассматривали методы assertTrue и assertFalse в прошлой статье, но здесь мы внесли некоторые изменения: указали сообщение, которое выведет PHPUnit если тест провалится, взамен стандартного “Failed asserting that is false”, которое не очень то помогает разобраться в проблеме.

Матемагия

Следующие тесты будут более математическими 🙂 Эти тесты помогают выяснить как соотносятся переданные переменные:

<?php

public function testValueEquals()
{
  $valueOne = 4;
  $valueTwo = 2;
  $this->assertEquals($this->_testable->addValues($valueOne,$valueTwo),6);
}
public function testValueGreaterThan()
{
  $valueOne = 4;
  $valueTwo = 2;
  $this->assertGreaterThan($valueTwo,$valueOne);
}
public function testLessThanOrEqual()
{
  $valueOne = 4;
  $valueTwo = 2;
  $this->assertLessThanOrEqual($valueTwo,$valueOne);
}
public function testAreObjectsEqual()
{
  $testTwo = new Testable();
  $this->_testable->resetMe = false;
  $this->assertEquals($this->_testable,$testTwo);
}

?>

Благодаря понятным названиям методов этот код практически не нуждается в объяснениях. Вы можете использовать такие методы как: assertGreaterThanassertLessThanassertGreaterThanOrEqual,assertLessThanOrEqual, и assertEquals.

Первый — assertEquals демонстрирует как вы можете использовать метод тестируемого класса для тестирования возвращаемых значений.

В этом примере есть еще один интересный пример использования метода assertEquals. С помощью этого метода мы можем сравнивать не только числовые значения, но и объекты, массивы и даже элементы DOM документов. Функция сравнивает все атрибуты объектов, но так как мы изменили свойство resetMe, объекты будут отличаться и assertEquals выдаст соответствующее сообщение: «Failed asserting that two objects are equal». Больше информации по использованию этого (и других тоже) метода можно посмотреть в документации.

Проверка строк

В PHPUnit есть также очень полезные методы для работы со строками. Эти функции могут сделать ваше тестирование проще, а код чище и более читаемым:

<?php
public function testStringEnding()
{
  $testString = $this->_testable->getTestString();
  $this->assertStringEndsWith('frood',$testString);
}

public function testStringStarts()
{
  $testString = $this->_testable->getTestString();
  $this->assertStringStartsWith('hoopy',$testString);
}

public function testEqualFileContents()
{
  $this->assertStringEqualsFile('/path/to/textfile.txt','foo');
}

public function testDoesStringMatchFormat()
{
  $testString = $this->_testable->getTestString();
  $this->assertStringMatchesFormat('%s',$testString);
}

public function testDoesStringFileFormat()
{
  $testString = $this->_testable->getTestString();
  $this->assertStringMatchesFormatFile('/path/to/textfie.txt','foo');
}

?>

Также как и с математическими методами, имена функций дают понять, что они проверяют. Первые два метода убеждаются в том, что строки заканчиваются или начинаются с указанных строк. Метод assertStringEqualsFile проверяет на равенство содержимое файла и переданной строки. Этот метод избавляет вас от необходимости самостоятельно считывать содержимое файла. Данный тест завершится успешно, если файл будет содержать строку "foo".

Следующие два метода, assertStringMatchesFormat и assertStringMatchesFormatFile, дают возможность проверять строки на соответствие заданному формату. Вы можете использовать предопределенные шаблоны (%e, %s, %S, %a, %A и т.д.), полный список которых вы можете найти в документации.

Рассмотрим еще два метода: assertNull и assertSame. И, как всегда, их имена довольно хорошо описывают то, что они делают. Пример:

<?php

public function testStringIsNotNull()
{
  $notANull = “i'm not a null!”;
  $this->assertNull($notANull);
}
public function testStringIsSame()
{
  $numberAsString = '1234';
  $this->assertSame(1234,$numberAsString);
}

?>

Первая проверка будет не успешна. Этот метод проверит значение переменной $notANull на равенство NULL, но в этой переменной у нас хранится строка.

Теперь поговорим о методе assertSame. Как вы знаете, PHP — язык с динамической типизацией, поэтому простая проверка на равенство числа 1234 и строки "1234" завершится успешно. Но метод assertEquals об этом знает и учитывает не только значение переменных, но и их тип. Поэтому второй тест тоже провалится.

Рассмотрим еще несколько полезных assert-методов, которые предоставляет PHPUnit:

<?php

public function testArrayKeyExists()
{
	$this->assertArrayHasKey('first key',$this->_testable->testArray);
}
public function testAttributeExists()
{
	$this->assertClassHasAttribute('resetMe',get_class($this->_testable));
}
public function testFileIsReal()
{
	$this->assertFileExists('/path/to/file.txt');
}
public function testIsInstance()
{
	$this->assertInstanceOf('OtherClass',$this->_testable);
}

?>

Эти методы позволяют проверять ключи в массивах, свойства классов, существование файлов и типы объектов. Первый тест просто проверит есть ли в нашем тестовом массиве ключ "first key". В следующем, благодаря методу assertClassHasAttribute, мы можем проверить имеет ли инстанс класса атрибут resetMe. Обратите внимание, что метод проверяет не существование атрибута у объекта, а его объявление в классе.

Метод assertFileExists — просто проверяет существует ли файл в файловой системе. Работа метода проходит по тем же правилам, что и работа стандартных файловых PHP функций: если к файлу нет доступа — тест провалится. И, наконец, assertInstanceOf, проверяет тип переменной на соответствие переданному значению. Конечно, нет отличий от такой конструкции: assertTrue($this->_testable instanceof OtherClass), но как я уже говорил использование встроенных функций PHPUnit сделает ваш код проще.

И последний тест, который мы сегодня рассмотрим:

<?php

public function testDoesMatchRegex()
{
  $testString = $this->_testable->getTestString();
  $this->assertRegExp('/[a-z]+/',$testString);
}

?>

Тест на соответствие регулярному выражению.

На этом все! В следующей статье мы рассмотрим работу с аннотациями и mock-объектами.

Хочешь получать статьи на почту?

Подпишись на обновления!
* Ваш email не будет разглашен/продан. Вы сможете отписаться в любое время.

1 Комментарий

  1. Дядюшка Чпокус:

    Браво! Отличный материал, спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *