2. 1. Типи тестів:
• Модульні тести (юніт тести)
Тести найменших одиниць функціональності (модулів). Перевірка, чи функція, яка тестується
виконує саме те, для чого вона реалізована.
• Інтеграційні тести
Поєднує різні одиниці коду (різні модулі). Перевірка чи правильно працює їхня комбінація.
• Системні тести
• Прийомочні тести
4. 3. Приклад модульного теста
use path/Default.php // підключення файла для тесту
class defaultTests extends PHPUnit_Framework_TestCase
{
private $default;
protected function setUp() // метод, який викликається перед кожним тестом (незалежно від кількості)
{
$this->default = new Default();
}
protected function tearDown() // метод, який викликається після кожного теста (незалежно від кількості)
{
$this-> default = NULL;
}
public function testAdd() // метод тест для метода add() класса Default()
{
$result = $this->default->add(1, 2);
$this->assertEquals(3, $result); //перевірка чи метод повернув правильний результат
}
}
5. 4. Запуск і результат тесту:
phpunit tests/DefaultTest.php
PHPUnit 3.7.32 by Sebastian Bergmann.
.
Time: 31ms, Memory: 2.25Mb
OK (1 test, 1 assertion)
6. 5. Використання різних наборів вхідних
данних
Без використання DataProvider
public function testAdd()
{
$result = $this->calculator->add(1, 2);
$this->assertEquals(3, $result);
}
public function testAddWithZero()
{
$result = $this->calculator->add(0, 0);
$this->assertEquals(0, $result);
}
public function testAddWithNegative()
{
$result = $this->calculator->add(-1, -1);
$this->assertEquals(-2, $result);
}
7. 6. Використання різних наборів вхідних
данних
Метод, який являється DataProvider’ом, має повертати масив масивів.
Метод, який являється тестом буде викликатись декілька раз з кожним масивом, в якості аргументів будуть
передаватись вміст масиви.
Ключові моменти для використання dataProvider:
- метод dataProvider має бути публічним.
- метод dataProvider має повертати масив зібраних данних
- метод теcта має використовувати аннотоацію @dataProvider, щоб вказати, який метод має використовуватись в
якості dataProvider
8. 7. Використання DataProvider
public function addDataProvider() {
return array(
array(1,2,3),
array(0,0,0),
array(-1,-1,-2),
);
}
/**
* @dataProvider addDataProvider
*/
public function testAdd($a, $b, $expected)
{
$result = $this->calculator->add($a, $b);
$this->assertEquals($expected, $result);
}
10. 9. Mock
Іноді при тестуванні нам потрібно використовувати обєкти, поведінку яких ми не можемо контролювати. Для рішення цієї проблеми
можна використовувати mock-обєкти.
Наприклад нам потрібно працювати з базою данних. Наприклад є метод який робить важкий запрос в базу данних, який
виконується не часто і для нашого тесту не потрібно відтворювати сам запрос, але результат запросу потрібен.
Метод getMock створює mock-обєкт з таким самим набором методів, як і звичайний обєкт классу Database. Всі методи, за
замовченням, будуть повертати null, але ми можемо перегрузити потрібний нам метод.
11. 9.1. Приклад використання Mock-обєкта
class Database {
/**
* повільний метод який буде викоритсаний
*/
public function reallyLongTime() {
$results = array(
array(1, 'test', 'foo value')
);
sleep(1000);
return $results;
}
}
Class DatabaseTest extends PHPUnit_Framework_TestCase {
private $db = null;
public function setUp() {
$this->db = new Database();
}
public function tearDown() {
unset($this->db);
}
public function testReallyLongReturn() {
$mock = $this->getMock('Database');
$result = array(
array(1, 'foo', 'bar test')
);
$mock->expects($this->any())
->method('reallyLongTime')
->will($this->returnValue($result));
$return = $mock->reallyLongTime();
$this→assertTrue(count($result) == 3);
}
}
При кожному виклику метода reallyLongTime обєкта $mock буде повертатись значення змінної $result, замість реального виконання методу.
12. 9.2 MockBuilder
При роботі з MockBuilder першим методом в ланцюгу викликів має бути getMockBuilder(), а останнім — getMock(). Наприклад, в наступному прикладі ми
створюємо mock-обєкт, которий не використовує конструктор, а також вимикає autoload:
function testReallyLongRunBuilder() {
$stub = $this->getMockBuilder('Database')
->setMethods(array(
'reallyLongTime'
))
->disableAutoload()
->disableOriginalConstructor()
->getMock();
$result = array(array(1, 'foo', 'bar test'));
$stub->expects($this->any())
->method('reallyLongTime')
->will($this->returnValue($result));
$this->assertGreaterThan(0, count($return));
}
13. public function getMock(
$originalClassName, // назва оригінального класса, для якого буде створений Mock обєкт
$methods = array(), // Массив методів для заміни, якщо вказати null методи будуть без підміни
array $arguments = array(), // аргументи, які передаються в конструктор
$mockClassName = '', // можна вказати імя Mock класа
$callOriginalConstructor = true, // відключити __construct()
$callOriginalClone = true, // відключити __clone()
$callAutoload = true // відключити __autoload()
);
$mock = $this->getMockBuilder('MyClass')
->setMethods(null)
->setConstructorArgs(array())
->setMockClassName('')
->disableOriginalConstructor()
->disableOriginalClone()
->disableAutoload()
->getMock();
Послідовність методів починається з метода getMockBuilder() и закінчується методом getMock() —
ці методи обовязкові.
14. 9.3. Очікування виклику методів
PHPUnit дозволяє нам контролювати кількість і порядок викликів підмінених методів. Для цього використовується конструкція
expects() з вказуванням потрібного метода за допомогою - method().
public function test_process() {
$mock = $this->getMock('MyClass', array('getTemperature', 'getWord', 'showWord'));
$mock->expects($this->once())->method('getTemperature');
$mock->expects($this->once())->method('showWord');
$mock->expects($this->once())->method('getWord');
$mock->process();
}
Результат виконання этого теста будет успешным, если при вызове метода process() произойдет однократный вызов трех
перечисленных методов: getTemperature(), getWord(), showWord(). Обратите внимание, что в тесте проверка вызова getWord()
стоит после проверки вызова showWord(), хотя в тестируемом методе наоборот. Все верно, ошибки здесь нет. Для контроля
порядка вызова методов в PHPUnit используется другая конструкция — at(). Поправим немного код нашего теста так чтобы
PHPUnit проверил заодно очередность вызова методов:
public function test_process() {
$mock = $this->getMock('MyClass', array('getTemperature', 'getWord', 'showWord'));
$mock->expects($this->at(0))->method('getTemperature');
$mock->expects($this->at(2))->method('showWord');
$mock->expects($this->at(1))->method('getWord');
$mock->process();
}
Помимо упомянутых once() и at() для тестирования ожиданий вызовов в PHPUnit есть также следующие конструкции: any(),
never(), atLeastOnce() и exactly($count). Их названия говорят сами за себя.