Vollständige Version anzeigen : Testen von eigenen Application Resourcen
Hallo,
kurz vorweg: hier im Forum fehlt meiner Meinung nach eine Kategorie für das Testen von ZF-Anwendungen...
Ich stehe vor dem Problem, dass ich eigene Application Resourcen testen möchte aber nicht so recht weiß wie ich das am besten machen soll. Mein derzeitiger Ansatz sieht so aus (bspw. an einer Resource für Cache):
class AppName_Application_Resource_CacheTest extends PHPUnit_Framework_TestCase
{
function setUp()
{
$this->_application = new Zend_Application('production', APPLICATION_PATH . '/configs/application.ini');
}
function tearDown()
{
unset($this->_application);
}
public function testInit()
{
$result = $this->_application->bootstrap();
$pluginResourceNames = $result->getBootstrap()->getPluginResourceNames();
$this->assertTrue(in_array('cache', $pluginResourceNames));
}
}
Nun ist es so das meine Testumgebung schon mit Zend_Application initialisiert wird. Wenn ich jetzt im Test noch ein Zend_Application-Objekt erstelle glaube ich das da irgendein Konflikt entsteht... jedenfalls bricht der Testdurchlauf an einer Stelle unmittelbar ab und das nur wenn ich diesen Test eingebunden habe, sonst nicht.
Wie kann ich also bzw. wie sollte man Application Resources sinnvoll testen?
Hast du schon geschaut wie und ob die vom ZF selbst getestet werden ?
Da es keine Zend_Application_Resource_Cache gibt, wird die wohl auch nicht von ZF getestet.
Es handelt sich dabei ja um eine eigene entwickelte Resource.
Ich meine damit doch, sich anzuschauen wie ANDERE resourcen vom ZF getestet werden und das zu übertragen auf deine eigenen, wie dort der setup-process genutzt wird, wenn du kollisionen mit anderen tests bei den variablen hast, schau dir vielleicht mal die seit phpunit 3.4beta vorhandenen annotations an:
Für eine ganze Test-Datei:
@runTestsInSeparateProcesses
für einzelne Tests:
@runInSeparateProcess
damit spart man sich nebenbei bei von der ZF-Test-Klasse abgeleiteten tests auch das ob_start(); und sieht den status :)
Achso sorry, da hatte ich dich missverstanden.
Ok hab grad mal geguckt bei den ZF Tests:
Dort wird das eigentlich auch mit dem Erzeugen eines Zend_Application -Objekts gemacht - aber die Testumgebung allgemein wird nicht mittels Zend_Application initialisiert...
Aber das ist nicht der Weg den ich haben möchte, denn schließlich ist Zend_Application u.a. genau für sowas gedacht, also eben schnell die Testumgebung zu initialisieren...
ice-breaker
25.05.2009, 14:43
Der Ansatz sieht doch schonmal gut aus, schau doch mal warum er abbricht. PHPUnit gibt da doch stack traces und alles bei fehlern aus.
für nen eigenen forenbereich für unit testing bin ich auch, aber nicht nur aufs zf bezogen
Ja leider gibt er mir an der Stelle garnichts aus.
Nur die üblichen Punkte (oder mit --verbose die Klassennamen) und dann kommt der Abbruch ohne Fehlerausgabe und ohne eine Zusammenfassung wieviele Assertions etc. durchgeführt wurden.
Ich weiß halt nur dass es an dieser Testklasse liegen muss. Wenn ich sie von den Tests ausschließe funktioniert ja alles korrekt.
ice-breaker
25.05.2009, 15:48
öhm, ich bin mir da nun nicht 100% sicher ob php vllt fehlertoleranter ist als ich denke, aber müsste du das protected attribut _application nicht auch definieren ?
Also das er nichts augibt kann ich mir schwer vorstellen.
Also daran liegts nicht weil ich es vorher drin stehen hatte. was grad auch nicht so recht warum ichs rausgenommen habe ^^.
Naja jedenfalls bekomm ich jetzt eine Fehlermeldung (obwohl mir nicht bewusst ist irgendwas geändert zu haben). Die Meldung besagt:
Zend_Application::__set_state(array(
'_autoloader' =>
Zend_Loader_Autoloader::__set_state(array(
'_autoloaders' =>
array (
),
'_defaultAutoloader' =>
array (
0 => 'Zend_Loader',
1 => 'loadClass',
),
'_fallbackAutoloader' => true,
'_internalAutoloader' =>
array (
0 =>
Zend_Loader_Autoloader::__set_state(array(
'_autoloaders' =>
array (
),
'_defaultAutoloader' =>
array (
0 => 'Zend_Loader',
1 => 'loadClass',
),
'_fallbackAutoloader' => true,
'_internalAutoloader' =>
array (
0 =>
Zend_Loader_Autoloader::__set_state(array(
'_autoloaders' =>
array (
),
'_defaultAutoloader' =>
array (
0 => 'Zend_Loader',
1 => 'loadClass',
),
'_fallbackAutoloader' => true,
'_internalAutoloader' =>
array (
0 =>
Zend_Loader_Autoloader::__set_state(array(
Fatal error: Nesting level too deep - recursive dependency? in [...]\tests\PHPUnit\Util\GlobalState.php on line 196
Call Stack:
0.0004 59992 1. {main}() [...]\tests\phpunit.php:0
0.3746 4791856 2. PHPUnit_TextUI_Command::main() [...]\tests\phpunit.php:52
0.9955 11724720 3. PHPUnit_TextUI_TestRunner->doRun() [...]\tests\PHPUnit\TextUI\Command.php:128
0.9994 11734344 4. PHPUnit_Framework_TestSuite->run() [...]\tests\PHPUnit\TextUI\TestRunner.php:348
1.7536 18210704 5. PHPUnit_Framework_TestSuite->run() [...]\tests\PHPUnit\Framework\TestSuite.php:648
1.7537 18213112 6. PHPUnit_Framework_TestSuite->run() [...]\tests\PHPUnit\Framework\TestSuite.php:648
1.7538 18215520 7. PHPUnit_Framework_TestSuite->run() [...]\tests\PHPUnit\Framework\TestSuite.php:648
1.7539 18217928 8. PHPUnit_Framework_TestSuite->runTest() [...]\tests\PHPUnit\Framework\TestSuite.php:687
1.7539 18217928 9. PHPUnit_Framework_TestCase->run() [...]\tests\PHPUnit\Framework\TestSuite.php:710
1.7556 18224016 10. PHPUnit_Util_GlobalState::getGlobalsAsString() [...]\tests\PHPUnit\Framework\TestCase.php:570
1.7576 18236240 11. var_export() [...]\tests\PHPUnit\Util\GlobalState.php:196
...
Hm, "rekursive Abhängigkeit" hört toll an :/
Also ich denke wirklich das liegt an der doppelten Zend_Application - Instanz (einmal die für die Testumgebung und einmal die für $this->_application)...
Oder hat jemand ne bessere Erklärung?
ice-breaker
25.05.2009, 17:44
sicher dass du keine cycles in deiner definion hast?
also zb logger hängt von config ab, und config von logger.
falls dem so nicht ist, kannst du mal testen ob auf solche abhängigkeiten geprüft wird?
wenn nicht mache ich da mal nen patch für, habe sowas gestern erst für was anderes gemacht.
Edit: Es gibt eine Prüfung auf solche Cycles, hmm
das mit ner Kategorie für unittests is ne gute idee. mal drüber schlafen wo das
reinpasst .. :)
gruß marco
DennisBecker
25.05.2009, 19:35
Unter "Testing, Installer & Backups" oder als eigener Punkt ;)
ice-breaker
25.05.2009, 19:49
Unter "Testing, Installer & Backups" oder als eigener Punkt ;)
Back-was? :D
Aber in den Sonstiges-Bereich, bitte.
DennisBecker
25.05.2009, 20:18
Ja, natürlich, ist ja nicht direkt ZF abhängig.
ice-breaker
25.05.2009, 21:02
Ja, natürlich, ist ja nicht direkt ZF abhängig.
neija der Großteil wird wahrscheinlich doch über Zend_Test gehen ^^
Mein Vorschlag: "Enterprise PHP: Unit-Testing, Continous Integration, (Agile) Dokumentation" :D
Fatal error: Nesting level too deep - recursive dependency? in [...]\tests\PHPUnit\Util\GlobalState.php on line 196
Wenn der Fehler nur mit xdebug auftritt, ist das wohl eine Einstellungssache von xdebug, Schau doch mal in der phpinfo wie hoch der Wert von xdebug ist, ich weis dass ich ihn als ich die Unittests fürs ZF angefangen hab irgendwann auch mal etwas hochsetzen musste.
ice-breaker
25.05.2009, 22:35
Das Maximum Nesting liegt afaik bei 128 oder 256 - also wenn man das übersteigt sollte man sich überlegen andere Algorithmen zu nutzen.
Hm, ich hatte doch was geändert und zwar gemäß dem Vorschlag von robo47 habe ich im Klassenkommentar das stehen:
/**
* @runTestsInSeparateProcesses
*/
class AppName_Application_Resource_CacheTest extends PHPUnit_Framework_TestCase
{
...
Erst dadurch kam die Fehlerausgabe zustande. Ohne ist es wie ich oben beschrieben habe, dass der Test ohne Ausgabe abbricht..
@ice-breaker:
Wie kann ich das mit den Cycles feststellen und evtl. beheben?
EDIT:
Also wenn ich in der Testklasse (mit der 'Annotation') alles rausnehme und nur einen leeren Testfall drinlasse bekomme ich trotzdem den Fehler. Scheint also gar nichts mit der eigentlichen Ursache dass der Test abbricht zu tun zu haben...
EDIT2:
Wenn ich xdebug deaktiviere bleibt das Verhalten gleich. Also mit @runTestsInSeparateProcesses kommt die 'nesting level to deep' Fehlermeldung und ohne gar keine Ausgabe.
donbosco
28.05.2009, 12:33
Ich habs folgendermaßen gemacht, eventuell hilft dir das weiter.
/**
* check that all resources are being loaded
* @see Zend_Application_Bootstrap_Bootstrap
* @see Zend Manual 4.5.5. Zend_Application_Bootstrap_Bootstrap
* @see Zend Manual 4.5.4 Zend_Application_Bootstrap_BootstrapAbstract
* @dataProvider resourceDataProvider
* @return void
*/
public function testApplicationResources($resource) {
// do the bootstrap
$this->application->bootstrap();
// get the bootstrap instance
$bootstrap = $this->application->getBootstrap();
// check resources
$this->assertTrue($bootstrap->hasResource($resource));
}
public function resourceDataProvider()
{
return array(
array('frontcontroller'),
array('db'),
array('cache'),
array('cachequery'),
array('config'),
array('domainconfig'),
array('layout'),
array('locale'),
array('modules'),
array('router'),
array('session'),
array('translate'),
array('view')
);
}
Erstmal noch ein Dankeschön an den Admin für den schönen neuen Testing&Deployment-Bereich! ^^
@donbosco:
Danke für den Vorschlag.
Ist ja im Prinzip ganz ähnlich bei dir... Leider kommt er bei mir nicht über $this->_application->bootstrap() hinaus - dort bricht er ab...
donbosco
28.05.2009, 17:24
Also ich hab eine Basis Testklasse die die Zend_Test Klassen erweitert. Schaut dann so aus:
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';
abstract class InitializationTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public $application;
public function setUp()
{
$this->application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$this->bootstrap = array($this, 'appBootstrap');
parent::setUp();
}
public function appBootstrap()
{
$this->application->bootstrap();
}
/**
* Cleans up the environment after running a test.
*/
protected function tearDown() {
#print_r($this->_request);
// TODO Auto-generated FooControllerTest::tearDown()
parent::tearDown ();
}
}
require_once 'InitializationTestCase.php';
class BootstrapTest extends InitializationTestCase {
/**
* Overwrite the appBootstrap method
* so that we can control the bootprocess
*
*/
public function appBootstrap() {}
/**
* call parent setup and initialize the test environment
*/
public function setUp() {
parent::setUp();
$_SERVER['SERVER_NAME'] = 'dein-server-name';
}
/**
* check that the environment is set
* @assert testing
*/
public function testEnvironmentSet() {
// do the bootstrap
$this->application->bootstrap();
$this->assertEquals('unittest', $this->application->getEnvironment());
}
/**
* check that all resources are being loaded
* @see Zend_Application_Bootstrap_Bootstrap
* @see Zend Manual 4.5.5. Zend_Application_Bootstrap_Bootstrap
* @see Zend Manual 4.5.4 Zend_Application_Bootstrap_BootstrapAbstract
* @dataProvider resourceDataProvider
* @return void
*/
public function testApplicationResources($resource) {
// do the bootstrap
$this->application->bootstrap();
// get the bootstrap instance
$bootstrap = $this->application->getBootstrap();
// check resources
$this->assertTrue($bootstrap->hasResource($resource));
}
.....
und hier kommen die DataProvider und was noch alles rein :)
}
Ich mache momentan noch alles ohne Zend_Test, denn ich habe das eigentlich so verstanden, dass man mit Zend_Test derzeit nur ControllerTests machen kann.
Hab ich da was falsch verstanden?
Kann man Zend_Test auch für ModelTests "missbrauchen"?
donbosco
29.05.2009, 11:31
Letzendlich kannst du mit Zend_Test alles machen was du mit PHPUnit auch machen kannst weil die Zend Test Klassen PHPUnit erweitern. Es gibt dennoch hier und da kleine Probleme. Wenn du also nur ganz normal Models testen willst, musst du natürlich nicht Zend_Test nehmen. Ich zum Beispiel erweitere immer Zend_Test und lass einfach den Dispatch Vorgang weg um ein Model zu testen.
Dann hast du alle Umgebungsvariablen, Includepfade und Sonstige so gesetzt wie du möchtest ohne den FrontController, ViewHelper und Co Overhead und kannst dann immer noch deine Models mit der nativen PHPUnit Methoden testen.
// resourcen laden
$this->application->bootstrap();
// kein dispatchen
$this->application->bootstrap()->run();
// instanzier dein model
$deinModel = new DeinModel();
// mach deine tests
....
vBulletin® v3.6.12, Copyright ©2000-2010, Jelsoft Enterprises Ltd.