2. Типи тестування
Модульне(Unit testing) — тестування одного окремого модуля (метода).
Інтеграційне(Integration Testing) — тестування групи взаємодіючих
модулів.
Системне (System Testing) — тестування системы вцілому.
3. Опис модульного тестування
Мета:
Ізолювати окремі частини програми і показати, що ці окремі модулі
працездатні.
Можливість безболісно проводити рефакторінг
«Живий документ» тестованого модуля (можливість швидко розібратись з
логікою модуля)
Перевірка якості коду модуля (всі можливі/неможливі вхідні/вихідні данні)
Типи:
Тести стану (state based) – перевіряють, що викликаний метод обєкта обробився
коректно, превіряючи стан тестованого обєкта після виклику метода.
Тести взаємодії (interaction tests) – тест в якому тестований обєкт виконує маніпуляції з
другими обєктами. Приміняються тоді, коли потрібно переконатись, що тестований
обєкт коректно взаємодіє з іншими обєктами.
4. Зовнішня залежність в тестах
Модульні тести:
Використання mock/stub обєктів, замість прямого звязку з БД, або іншими
зовнішніми системами.
Інтеграційні тести:
Використання прямих підключень і запитів до БД з використанням
транзакцій, або до інших зовнішніх систем.
5. Приклад модульного тесту
Tender {
public function checkDate($date){
if($date > date(‘d-m-Y’, time())){
return ‘success’;
} else {
return ‘error’;
}
}
}
TenderTest {
public function testCheckDate() {
$date = date(‘d-m-Y’, time()+3600); //null, another date format, date with spaces
$result = (new Tender())->checkDate($date);
$this->assertTrue($result == ‘success’);
}
}
6. Data provider
Dataprovider використовується для різних наборів вхідних данних.
Метод, який являється DataProvider’ом, має повертати масив масивів, або обєкт.
Метод, який являється тестом буде викликатись декілька раз з кожним масивом,
в якості аргументів будуть передаватись вміст масиву.
Ключові моменти для використання dataProvider:
- dataProvider має бути статичним публічним методом.
- dataProvider має повертати масив зібраних данних
- тест має використовувати аннотацію @dataProvider, щоб вказати, який метод
має використовуватись в якості dataProvider
7. Приклад використання data provider
Public static function dateProvider(){
return [
[date(‘d-m-Y’, time()), ‘error’],
[date(‘d-m-Y’, time()+3600), ‘success’],
[null, ‘error’]
];
}
/**
* @dataProvider dateProvider
*/
public function testCheckDate($date, $result) {
$res = (new Tender())->checkDate($date);
$this->assertTrue($res == $result);
}
9. Приклад використання asserts
public function testCreateDate() {
$date = date(‘d-m-Y’,time()+3600);
$res = (new Tender())→createDate($date);
$message1 = ‘createDate() не повертає обєкт, классу
Date()’;
$message2 = ‘в классі Date() немає аттрибуту $formDate’;
$message3 = ‘метод createDate() не відформатував
$date’;
$this→assertInstanceOf(Date(), $res, $message1);
$this→assertClassHasAttribute(‘formatDate’, ‘Date’,
$message2);
$this→assertTrue($date != $res→formatDate, $message3)
}
public function createDate($date){
if($date > date(‘d-m-Y’, time())){
return new Date($date);
} else {
return null;
}
}
Class Date{
public $formatDate;
public function __constructor($date){
$this→formatDate = date(‘Y-m-d’, strtotime($date));
}
}
10. Залежності тестів
Producer – тестовий метод, котрий повертає значення,
від яких залежать інші методи модуля.
Consumer – тестовий метод, котрий залежить від
одного або більше методів (producer) і значень, які
вони повертають.
Для позначенняя залежного метода, використовується
анотація @depends
public function testEmpty(){
$stack = array();
$this->assertTrue(empty($stack));
return $stack;
}
/**
* @depends testEmpty
*/
public function testPush(array $stack) {
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertFalse(empty($stack));
return $stack;
}
/**
* @depends testPush
*/
public function testPop(array $stack)
{
$this->assertEquals('foo', array_pop($stack));
$this->assertTrue(empty($stack));
}
}
11. Тестування виключень (exception)
Метод який повертає exeption
public function returnException() {
$this->setException('InvalidArgException');
}
Перевірка exeption в тесті
/**
* @expectedException InvalidArgException<br>
*/
public function testException() {
(new SomeClass())->returnException();
$this->assertTrue(true);
}
12. Mock, Stub класи заглушки (дублери)
Коли потрібно використовувати класи дублери:
Необхідність можливості запуску тестів незалежно де (на машині любого розробника в любій ОС)
Коли результат запиту до БД/api нам стовідсотково відомий і нам не потрібно його перевіряти.
Низька швидкість виконання тестів з реальними обєктами (наприклад робота з БД, файламию поштовим сервером,
сторонніми api)
Типи класів дублерів:
Dummy – обєкти котрі передаються в методиб але насправді не використовуються. В основному це параметри методів
(якщо вони не впливають в тесті на те, що ми хочемо перевірити). Іноді це просто null.
Fake – це обєкти, котрі мають внутрішню реалізацію, але зазвичай вона урізана і їх не можна використовувати в
готовому коді.
Stubs – обеспечують чітко зашиту відповідб на виклик під час тестування. Приміняються для заміни тих обєктів, які
забезпечують тест вхідними данними. Також вони можуть зберігати в соб інформацію про виклики (наприклад
параметри і кількість викликів).
Mocks – обєкти, котрі налаштовуються (наприклад специфічно під кожен тест) і дозволяють задавати очікування в
вигляді свого роду специфікації викликів, які ми плануємо отримати.
Mock – використовується для тестів на поведінку обєкта.
Всі інші дублі використовуються для тестуванння стану обєкта після виклику метода.
13. Методи по роботі з класами дублями (Mock)
public function getMock(
$originalClassName, // назва оригінального класу, для котрого буде
створений Mock объект
$methods = array(), // масив методів які будуть замінені
array $arguments = array(), // аргументи, як передаються в конструктор
$mockClassName = '', // можна вказать імя Mock класа
$callOriginalConstructor = true, // відключення виклику конструктора
$callOriginalClone = true, // відключення виклику __clone()
$callAutoload = true // відключення виклику __autoload()
);
Якщо другим аргументом в getMock() передати null, тоді жоден метод не
буде замінений.
Додаткові способи виклику Mock обєкта:
$mock = $this->getMockBuilder('MyClass')
->setMethods(null)
->setConstructorArgs(array())
->setMockClassName('')
->disableOriginalConstructor()
->disableOriginalClone()
->disableAutoload()
→getMock();
getMockClass() — створює Mock класс і повертає його назву у вигляді
строки;
getMockForAbstractClass() — повертає Mock обєкт абстрактного класа, в
котрому замінені всі абстрактні методи.
14. Очікування виклику метода
PHPUnit дозволяєє нам контролювати кількість і порядок викликів підмінених методів, для цього використовується
метод expects()
public function test_process() {
$mock = $this->getMock('MyClass', array('getTemperature', 'getWord'));
$mock->expects($this->once())->method('getTemperature');
$mock->expects($this->once())->method('getWord');
$mock->process();
}
Результат виконання цього тесту буде успішним, якщо при виклику метода process() відбудеться одноразовий виклик двох
вказаних методів: getTemperature(), getWord(). В такому випадку неважливий порядок виклику методів.
Для контроля порядка виклику методів в PHPUnit використовується друга конструкція — at().
public function test_process() {
$mock = $this->getMock('MyClass', array('getTemperature', 'getWord'));
$mock->expects($this->at(1))->method('getTemperature');
$mock->expects($this->at(0))->method('showWord');
$mock->process();
}
Окрім once() і at() для тестування очікування викликів в PHPUnit є також наступні конструкції: any(), never(), atLeastOnce() и
exactly($count).
15. Перевизначення (заміна) методів Mock обєктів
Основною можливітю Mock обєктів, являється можливість заміни методів.
Для перевизначення методів використовується метод will():
$temperature = 20;
$mock→expects($this→once())→method('getTemperature')→will($this→returnValue($temperature));
Перевірка вказаних аргументів:
Ще одна корисна для тестування можливість Mock обєктів являється перевірка аргументів, вказанных при виклику
подміненого метода, за допомогою конструкції with():
public function test_with_and_will_usage() {
$mock = $this->getMock('MyClass', array('getWord'));
$mock->expects($this->once())
->method('getWord')
->with($this->greaterThan(25))
->will($this->returnValue('hot'));
$this->assertEquals('hot', $mock->getWord(30));
}
В якості аргументів метод with() може приймати такі самі конструкції, що і перевірка assertThat():
attribute(), anything(), arrayHasKey(), contains(), )fileExists(), greaterThan(), classHasAttribute(), classHasStaticAttribute(), hasAttribute(),
isFalse(), isInstanceOf(), isNull(), isTrue(), isType(), matchesRegularExpression(), stringContains()
16. Приклад використання mock обєкта.
Class Tender{
public function getCategories(){
return (new Category)→getList(); //request to DB
}
public function checkCategory($category){
return in_array($category, $this->getCategories());
}
}
private $_categories = [1,2,3,4]
public function testCheckCategories(){
$mock = $this→getMock(‘Tender’, [‘getCategories’]);
$this→assertInstanceOf(‘Tender’, $mock);
$mock→expects($this→once())
→method(‘getCategories’)→returnValue($this→
->returnValue($this->_categories);
}
17. Тестове оточення (fixtures).
Так як після завершення тесту всім змінним потрібно повернути попередні значення, створюється
тестове оточення (fixture).
Для її створення використовуються методи:
До того як почне виконувтись тест, викликається метод setUp();
Як тільки тест завершиться, викликається метод tearDown();
Також є інші методи, які викликаються в такій послідовності:
setUpBeforeClass()
setUp();
assertPreConditions();
testOne(); // тест
assertPostConditions();
tearDown();
18. Транзакції в тестуванні
Транзакції зазвичай використовуються в інтеграційних тестах, в модульних використовуюься mock
обєкти для звязку з БД.
public function setUp()
{
parent::setUp();
DB::beginTransaction();
}
public function tearDown()
{
DB::rollBack();
parent::tearDown();
}
19. MVC
Модель — містить бізнес-логіку додатку. Включає методи виборки,
обробки і надання конкретних данних, що зазвичай робить її дуже
великою, це нормально.
Вид — використовується для відображення данних, отриманих з
контроллера і моделі.
Вид містить HTML и невеликі вставки PHP-кода для форматування і
відображення данних.
Не повинен напряму звертатись до БД.
Не повинен працювати з данними, отриманими з запита користувача.
Може напряму звертатись властивостей і методів контролера чи
моделі, для отримання готових данних.
Контролер — звязок моделі і виду. Контроллер відповідає за обробку
запросів користувача. Контроллер не повинен містити SQL-запросів.
Контроллер не повинен містити HTML.
В гарно спроектованому MVC-додатку контролери зазвичай дуже
маленькі і містять лише декілка десятків строк коду.
Модели, навпаки, дуже товсті і містять більшу частину кода, повязану з