„Codeception Unit Tesztelés segédlet” változatai közötti eltérés

Innen: Szitár-Net Wiki
Ugrás a navigációhoz Ugrás a kereséshez
 
(11 közbenső módosítás ugyanattól a szerkesztőtől nincs mutatva)
16. sor: 16. sor:
 
2. A config mappában a test_db.php fájlon belül kell megadnunk a teszt adatbázis nevét (pl: dbname=c1_matrix_test)
 
2. A config mappában a test_db.php fájlon belül kell megadnunk a teszt adatbázis nevét (pl: dbname=c1_matrix_test)
  
'''Hasznos tudnivaló:''' Minden egyes teszt metódusban / tesztesetnél elvégzett adatbázis művelet visszavonásra (rollback) kerül '''automatikusan'''. Így mindig ugyanolyan adatokkal tudunk dolgozni. A használatához nem kell módosításokat eszközölni, alap beállítás szerint így működik.
+
'''Hasznos tudnivaló:''' Minden egyes teszt metódusban / tesztesetnél elvégzett adatbázis művelet visszavonásra kerül '''automatikusan''' (transaction rollback). Így mindig ugyanolyan adatokkal tudunk dolgozni. A használatához nem kell módosításokat eszközölni, alap beállítás szerint így működik.
  
 
== Tesztek írása ==
 
== Tesztek írása ==
33. sor: 33. sor:
 
=== Assert / Állítás ===
 
=== Assert / Állítás ===
 
[[Fájl:Unit11.png|400px|bélyegkép|jobbra|Példa teszt futtatása során hibás assertEqual()-ra]]
 
[[Fájl:Unit11.png|400px|bélyegkép|jobbra|Példa teszt futtatása során hibás assertEqual()-ra]]
Az assertek segítségével vizsgálhatjuk meg a kívánt elemet. A Codeception ellenőrzi az assertek visszatérési értékét. Majd ezek alapján összegzi a tesztek eredményét. Példa néhány assert-re:
+
Az assertek segítségével vizsgálhatjuk meg a kívánt elemet. A Codeception ellenőrzi az assertek helyességét. Majd ezek alapján összegzi a tesztek eredményét. Példa néhány assert-re:
 
* '''$this->assertTrue(1 == 1, ”egyenlőség vizsgálat”)''': 1. paraméter a feltétel, ha igaz akkor a teszt / assert sikeres volt | 2. paraméter az assert leírása
 
* '''$this->assertTrue(1 == 1, ”egyenlőség vizsgálat”)''': 1. paraméter a feltétel, ha igaz akkor a teszt / assert sikeres volt | 2. paraméter az assert leírása
 
* '''$this->assertEqual(4, 3+2, ”összeadás”)''': 1. paraméter az elvárt érték, a 2. paraméter a vizsgálandó érték, ha a két paraméter egyenlő akkor teszt / assert sikeres volt | 3. paraméterben szintén megadható az assert leírása<br><br><br>
 
* '''$this->assertEqual(4, 3+2, ”összeadás”)''': 1. paraméter az elvárt érték, a 2. paraméter a vizsgálandó érték, ha a két paraméter egyenlő akkor teszt / assert sikeres volt | 3. paraméterben szintén megadható az assert leírása<br><br><br>
 +
 +
=== Test doubles ===
 +
Összefoglaló név, kettő különböző módszert foglal magába. Segítségével tudjuk szimulálni a különböző metódusokat. Így a tesztelni kívánt metódus szeparálását tudjuk megvalósítani.<br>
 +
Például: Ha a metódusom egy másik metódust hív meg, akkor bármilyen művelettel helyettesíthetem azt. Ha pl. csak egy boolean-t visszaadó metódusról van szó akkor, egyszerűen csak mondhatom azt, hogy legyen true.<br>
 +
Módszerei:
 +
* '''stub:''' A szimulálandó osztályt, azon belül pedig a metódusokat hozhatjuk létre vele. Lehetőségünk van pl. a <code>__constructor()</code> metódus figyelmen kívül hagyására.
 +
* '''mock:''' Nagyon hasonló a stub-hoz, annyi a különbség, hogy ellenőrzést is végez, hogy biztosan meglett-e hívva a metódus. expects() és with() metódusok használata
 +
<pre>
 +
$stub = $this->getMockBuilder('app\modules\tp\models\TpNew')
 +
->disableOriginalConstructor()
 +
->getMock();
 +
 +
$stub->method('GetIsInitialized')
 +
->willReturn(true);
 +
</pre>
 +
'''Fontos:''' A static, protected és final metódusokat nem lehet "stubbolni" vagy "mockolni"<br>
 +
 +
=== Hozzáférés private és protected metódusokhoz, attribútumokhoz ===
 +
A private vagy protected metódusok meghívásához, szükséges a metódus hozzáférését true értékre állítani, attribútumok esetében is hasonlóan kell eljárni. Az alábbi metódusok alapján:<br>
 +
 +
''// Teszt osztályon belül''
 +
<pre>
 +
//metódus hozzáférés
 +
protected static function getMethod($name): ReflectionMethod
 +
{
 +
$class = new ReflectionClass('app\modules\tp\models\TpNew');
 +
$method = $class->getMethod($name);
 +
$method->setAccessible(true);
 +
return $method;
 +
}
 +
 +
//változó hozzáférés
 +
protected static function getProperty($name): ReflectionProperty
 +
{
 +
$class = new ReflectionClass('app\modules\bonus\models\Bonus');
 +
$property = $class->getProperty($name);
 +
$property->setAccessible(true);
 +
return $property;
 +
}
 +
</pre>
 +
 +
''// A teszt metóduson belül''
 +
<pre>
 +
// Metódus
 +
$getWorkingMethod = self::getMethod('getWorkings');
 +
$model = new TpNew();
 +
$getWorkingsData = $getWorkingMethod->invokeArgs($model, ['2021-04-01 10:20:30']);
 +
 +
// Attribútum
 +
$workingsProperty = self::getProperty('workings');
 +
$model = new TpNew();
 +
$workingsProperty->setValue($model, $data);
 +
$workingsProperty->getValue($model);
 +
</pre>
  
 
=== Fixtures ===
 
=== Fixtures ===
 
A teszt adatbázis adatokkal való feltöltésében segít. Olyan osztályok, amelyeket a setUp() metódus dinamikusan betölt. Használatával megvalósítható a teszt adatbázis automatikus törlése / ürítése minden teszteset után. Nekünk csak a ”fixture” osztályt, illetve az adatbázisba feltölteni kívánt adatokat kell létrehoznunk<br>
 
A teszt adatbázis adatokkal való feltöltésében segít. Olyan osztályok, amelyeket a setUp() metódus dinamikusan betölt. Használatával megvalósítható a teszt adatbázis automatikus törlése / ürítése minden teszteset után. Nekünk csak a ”fixture” osztályt, illetve az adatbázisba feltölteni kívánt adatokat kell létrehoznunk<br>
  
'''Fixture osztály létrehozása'''
+
==== Fixture osztály létrehozása ====
 
# A tests\unit\fixtures mappában <Osztálynév>Fixture.php fájl létrehozása
 
# A tests\unit\fixtures mappában <Osztálynév>Fixture.php fájl létrehozása
 
# A létrehozandó fixture osztályt ki kell "extendelni" az ActiveFixture osztállyal
 
# A létrehozandó fixture osztályt ki kell "extendelni" az ActiveFixture osztállyal
55. sor: 109. sor:
 
</pre>
 
</pre>
  
'''Fixture adatok létrehozása'''<br>
+
==== Fixture adatok létrehozása ====
 
A unit \ fixtures \ data mappában kell létrehoznunk egy '''<adatbázis tábla neve>.php''' nevű fájlt. Abban pedig return [] formában megkell adnunk a feltölteni kívánt adatokat. A táblák sorának itt megadhatunk kulcsot is. Így pl. a <code> $this->tester->grabFixture('adatbázis tábla neve', 'kulcs');</code> segítségével könnyen elérhetjük a kívánt adatokat.
 
A unit \ fixtures \ data mappában kell létrehoznunk egy '''<adatbázis tábla neve>.php''' nevű fájlt. Abban pedig return [] formában megkell adnunk a feltölteni kívánt adatokat. A táblák sorának itt megadhatunk kulcsot is. Így pl. a <code> $this->tester->grabFixture('adatbázis tábla neve', 'kulcs');</code> segítségével könnyen elérhetjük a kívánt adatokat.
 
<pre>
 
<pre>
71. sor: 125. sor:
 
</pre>
 
</pre>
  
'''Fixtures betöltése, és használata'''
+
==== Fixtures betöltése, és használata ====
 
<pre>
 
<pre>
 
class PartnersTest extends Unit
 
class PartnersTest extends Unit
95. sor: 149. sor:
 
}
 
}
 
}
 
}
 +
</pre>
 +
 +
== Certifikáció / Coverage ==
 +
[[Fájl:Cov1.png|300px|bélyegkép|jobbra|Wamp php.ini elérése]]
 +
Lehetőségünk van a lefedettség (coverage) kimutatására. Mind xml, mind html formában.<br>
 +
1. Wamp php.ini legaljáról az xDebug szekció kimásolása, majd wamp64\bin\php\php<aktuális verzió> mappában a php.ini fájl aljára beilleszteni a másolt tartalmat. xDebug paraméter beállítása: xdebug.mode =develop,debug,coverage. Végül a Wamp újraindítása. <br>
 +
2. A projektnek a codeception.yml fájljába:
 +
<pre>
 +
coverage:
 +
    enabled: true </pre>
 +
3. Majd Coverage készítése: <br>
 +
'''<code> vendor\bin\codecept run --coverage-xml </code>'''<br>
 +
'''<code> vendor\bin\codecept run --coverage-html </code>'''<br>
 +
[[Fájl:Cov2.png|600px|bélyegkép|középre|Html coverage a böngészőben]]
 +
 +
=== SonarQube config ===
 +
//sonar-project.properties
 +
<pre>
 +
sonar.exclusions=**/webfonts/**,**/vendor/**,**/uploads/**,**/runtime/**,**/backups/**,**/assets/**,**/.scannerwork/**,**/tests/**
 +
sonar.tests=tests
 +
sonar.php.coverage.reportPaths=tests/_output/coverage.xml
 
</pre>
 
</pre>
  

A lap jelenlegi, 2023. május 26., 11:34-kori változata

A Yii2 keretrendszer automatikusan feltelepíti a Codeception nevű PHP teszt keretrendszert, mely PhpUnit-ot használ. A terminálba a beírt vendor\bin\codecept parancs segítségével megtekinthetjük a kiválasztható opciókat.

Unit teszt osztály létrehozása

A tesztelni kívánt osztály metódusainak generálása

Terminálba írható parancs segítségével: vendor\bin\codecept g:test unit HolidaysTest
vagy pedig:

1. Grafikus felületen tests mappában unit mappára jobb klikk > new > PHP Test > Codeception Unit Test
2. Ezekután bejön a Create New PHP Test ablaka: Itt megadhatjuk, hogy melyik megírt osztályt szeretnénk tesztelni, illetve az adott osztály tesztelni kívánt metódusait is legenerálhatjuk.

Teszt adatbázis létrehozása

Az ActiveRecord modellek hatékony teszteléséhez szükséges a külön teszt adatbázis létrehozása
Fontos: Ne a fejlesztői vagy az éles adatbázist használjuk, mert az adatok törlődhetnek.
1. Teszt adatbázis és a megfelelő táblák létrehozása
2. A config mappában a test_db.php fájlon belül kell megadnunk a teszt adatbázis nevét (pl: dbname=c1_matrix_test)

Hasznos tudnivaló: Minden egyes teszt metódusban / tesztesetnél elvégzett adatbázis művelet visszavonásra kerül automatikusan (transaction rollback). Így mindig ugyanolyan adatokkal tudunk dolgozni. A használatához nem kell módosításokat eszközölni, alap beállítás szerint így működik.

Tesztek írása

Unit osztályból örökölt metódusok

Lehetőségünk van, a teszt / tesztesetek futtatása előtt és után beépített segéd metódusok lefuttatására. Ilyenek például:

  • setUpBeforeClass() - A teszt osztály működtetése előtt lefutó metódus
  • _before() - A teszt osztályban csak egyszer fut le, a tesztesetek előtt
  • setUp() - Minden teszteset előtt lefut
  • tearDown() - Minden teszteset után lefut
  • _after() - A teszt osztályban csak egyszer fut le, a tesztesetek után
  • tearDownAfterClass() - A teszt osztály működtetése után lefutó metódus

Példa setUp() metódus használatára:
A setUp() segítségével minden egyes teszteset előtt lehetőségünk van egy új partner objektum létrehozására, illetve a korábbi adatok törlésére, így szeparáltan tudjuk tesztelni az egységet. A képen látható módon létrehozhatunk privát változót, melyet felhasználhatunk a tesztek során.

Példa setUp() használatára

Assert / Állítás

Példa teszt futtatása során hibás assertEqual()-ra

Az assertek segítségével vizsgálhatjuk meg a kívánt elemet. A Codeception ellenőrzi az assertek helyességét. Majd ezek alapján összegzi a tesztek eredményét. Példa néhány assert-re:

  • $this->assertTrue(1 == 1, ”egyenlőség vizsgálat”): 1. paraméter a feltétel, ha igaz akkor a teszt / assert sikeres volt | 2. paraméter az assert leírása
  • $this->assertEqual(4, 3+2, ”összeadás”): 1. paraméter az elvárt érték, a 2. paraméter a vizsgálandó érték, ha a két paraméter egyenlő akkor teszt / assert sikeres volt | 3. paraméterben szintén megadható az assert leírása


Test doubles

Összefoglaló név, kettő különböző módszert foglal magába. Segítségével tudjuk szimulálni a különböző metódusokat. Így a tesztelni kívánt metódus szeparálását tudjuk megvalósítani.
Például: Ha a metódusom egy másik metódust hív meg, akkor bármilyen művelettel helyettesíthetem azt. Ha pl. csak egy boolean-t visszaadó metódusról van szó akkor, egyszerűen csak mondhatom azt, hogy legyen true.
Módszerei:

  • stub: A szimulálandó osztályt, azon belül pedig a metódusokat hozhatjuk létre vele. Lehetőségünk van pl. a __constructor() metódus figyelmen kívül hagyására.
  • mock: Nagyon hasonló a stub-hoz, annyi a különbség, hogy ellenőrzést is végez, hogy biztosan meglett-e hívva a metódus. expects() és with() metódusok használata
$stub = $this->getMockBuilder('app\modules\tp\models\TpNew')
			->disableOriginalConstructor()
			->getMock();

$stub->method('GetIsInitialized')
	->willReturn(true);

Fontos: A static, protected és final metódusokat nem lehet "stubbolni" vagy "mockolni"

Hozzáférés private és protected metódusokhoz, attribútumokhoz

A private vagy protected metódusok meghívásához, szükséges a metódus hozzáférését true értékre állítani, attribútumok esetében is hasonlóan kell eljárni. Az alábbi metódusok alapján:

// Teszt osztályon belül

//metódus hozzáférés
protected static function getMethod($name): ReflectionMethod
	{
		$class = new ReflectionClass('app\modules\tp\models\TpNew'); 
		$method = $class->getMethod($name);
		$method->setAccessible(true);
		return $method;
	}

//változó hozzáférés
protected static function getProperty($name): ReflectionProperty
	{
		$class = new ReflectionClass('app\modules\bonus\models\Bonus');
		$property = $class->getProperty($name);
		$property->setAccessible(true);
		return $property;
	}

// A teszt metóduson belül

// Metódus
$getWorkingMethod = self::getMethod('getWorkings');
$model = new TpNew();
$getWorkingsData = $getWorkingMethod->invokeArgs($model, ['2021-04-01 10:20:30']);

// Attribútum
$workingsProperty = self::getProperty('workings');
$model = new TpNew();
$workingsProperty->setValue($model, $data);
$workingsProperty->getValue($model);

Fixtures

A teszt adatbázis adatokkal való feltöltésében segít. Olyan osztályok, amelyeket a setUp() metódus dinamikusan betölt. Használatával megvalósítható a teszt adatbázis automatikus törlése / ürítése minden teszteset után. Nekünk csak a ”fixture” osztályt, illetve az adatbázisba feltölteni kívánt adatokat kell létrehoznunk

Fixture osztály létrehozása

  1. A tests\unit\fixtures mappában <Osztálynév>Fixture.php fájl létrehozása
  2. A létrehozandó fixture osztályt ki kell "extendelni" az ActiveFixture osztállyal
  3. A $modelClass változónak megadni a feltölteni kívánt adatbázis ActiveRecord osztályának elérését (namespace-t)
<?php
namespace unit\fixtures;
use yii\test\ActiveFixture;

class PartnersFixture extends ActiveFixture
{
	public $modelClass = 'app\modules\partners\models\Partners';
}

Fixture adatok létrehozása

A unit \ fixtures \ data mappában kell létrehoznunk egy <adatbázis tábla neve>.php nevű fájlt. Abban pedig return [] formában megkell adnunk a feltölteni kívánt adatokat. A táblák sorának itt megadhatunk kulcsot is. Így pl. a $this->tester->grabFixture('adatbázis tábla neve', 'kulcs'); segítségével könnyen elérhetjük a kívánt adatokat.

<?php
return [
	1 => [
		'name' => 'tesztCég1',
		'note' => 'teszt megjegyzés'
	],
	2 => [
		'name' => 'tesztCég2',
		'note' => 'teszt megjegyzés2'
	]
];

Fixtures betöltése, és használata

class PartnersTest extends Unit
{
	/** @var Partners */
	private $_partner = null;

	/** @var UnitTester */
	protected $tester;

	public function _fixtures(): array
	{
		return [
			'partners' => PartnersFixture::className(),
			'contact' => ContactFixture::className(),
			'contact_event' => ContactEventFixture::className()
		];
	}
	protected function _setUp()
	{
		parent::_setUp();
		$this->_partner = $this->tester->grabFixture('partners', 1);
	}
}

Certifikáció / Coverage

Wamp php.ini elérése

Lehetőségünk van a lefedettség (coverage) kimutatására. Mind xml, mind html formában.
1. Wamp php.ini legaljáról az xDebug szekció kimásolása, majd wamp64\bin\php\php<aktuális verzió> mappában a php.ini fájl aljára beilleszteni a másolt tartalmat. xDebug paraméter beállítása: xdebug.mode =develop,debug,coverage. Végül a Wamp újraindítása.
2. A projektnek a codeception.yml fájljába:

 
coverage:
    enabled: true 

3. Majd Coverage készítése:
vendor\bin\codecept run --coverage-xml
vendor\bin\codecept run --coverage-html

Html coverage a böngészőben

SonarQube config

//sonar-project.properties

sonar.exclusions=**/webfonts/**,**/vendor/**,**/uploads/**,**/runtime/**,**/backups/**,**/assets/**,**/.scannerwork/**,**/tests/**
sonar.tests=tests
sonar.php.coverage.reportPaths=tests/_output/coverage.xml

Gyakori hibák

Configuration.php: _bootstrap.php cant't be loaded

bootstrap can't be loaded

Ha a vendor\bin\codecept bármilyen parancsa beírása esetén az alábbi képen is látható hiba üzenet jelenik meg, akkor valószínűleg a tests mappa törölve lett, a codeception.yml viszont nem
Megoldás:

  1. A codeception.yml fájl törlése
  2. A terminálban a php composer.phar require codeception/module-phpbrowser --dev parancs beírása, a module-phpbrowser telepítése / frissítése
  3. A terminálban a vendor\bin\codecept bootstrap parancs futtatása

Ezekután létrehozhatjuk a tesztjeinket.

Class not found

Class not found

A vendor\bin\codecept run futtatása után a következő hiba üzenet fogad:
Megoldás: A hiányzó osztály és az Apps.php fájl require paranccsal történő implementálása a _bootstrap.php fájlban, az alább is látható módon:
require(__DIR__ . '\..\modules\partners\models\Partners.php');
require(__DIR__ . '\..\modules\apps\models\Apps.php');

Class 'Yii' not found

Class 'Yii' not found

Ha az alábbi hibaüzenet jelenik meg akkor, a codeception.yml fájl konfigurálása szükséges.
Megoldás: A codeception.yml fájlhoz az alábbi kód hozzáadása:

bootstrap: _bootstrap.php
modules:
    config:
        Yii2:
            configFile: 'config/test.php'

_bootstrap.php-nak az alábbiakat kell tartalmaznia kell:

<?php
define('YII_ENV', 'test');
defined('YII_DEBUG') or define('YII_DEBUG', true);

require_once __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
require __DIR__ .'/../vendor/autoload.php';

Call to a member function getDb() on null || Fixture használata során fellépő hiba esetén

[Error] Call to a member function getDb() on null

A hiba esetén, a unit.suite.yml fájl konfigurálása szükséges.
Megoldás: A unit.suite.yml fájlhoz az alábbi kód hozzáadása, majd a vendor\bin\codecept build parancs futtatása

modules:
    enabled:
        - Yii2:
            part: [ orm, email, fixtures ]


Unknown database 'yii2_basic_tests'

[1049] Unknown database 'yii2_basic_tests'

Megoldás: Ebben az esetben a config mappában a test_db.php-ban a dbname beállítása szükséges.
Fontos: ne a fejlesztés adatbázisát használjuk, hozzunk létre külön tesztadatbázist