When I develop a TYPO3 ExtBase extension, I often use the
Testing Framework shipped with the TYPO3 extension
PHPUnit. With the Testing Framework it is possible to create real
testrecords in the TYPO3 database and use those records in your tests to verify everything works as expected.
In a project I faced the situation, that one of my repository methods did not find the records created with the Testing Framework. Well, after some hours of debugging I finally found the solution.
The problem
I created a new TYPO3 ExtBase extension with some tables, a controller and a view. Really nothing special about that. I then created a function in my repository which searches for some records in the database with a configurable limit.
/**
* Returns the latest records.
*
* @param int $limit The Limit
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findLatest($limit) {
$query = $this->createQuery();
$query->setOrderings(array('datecreated' => QueryInterface::ORDER_DESCENDING));
$query->setLimit((integer)$limit);
$records = $query->execute();
return $records;
}
Then I started to create some tests for the newly created function in the repository. One test was as following.
/**
* Test if findLatest returns expected amout of records if limit given
*
* @test
* @return void
*/
public function findLatestReturnsLimitedResult() {
$this->testingFramework->createRecord('tx_myext_domain_model_table', array('datecreated' => time()));
$this->testingFramework->createRecord('tx_myext_domain_model_table', array('datecreated' => time()));
$this->testingFramework->createRecord('tx_myext_domain_model_table', array('datecreated' => time()));
$this->createTestRecords();
$this->assertEquals(2, $this->myRepository->findLatest(2)->count());
}
In the test above, 3 records with the Testing Framework are created and I'll use the function findLatest with a given limit of 2 to test, if really 2 records are returned.
Well, actually findLatest did
return 0 records. I then created some real records in the TYPO3 backend and verified, that findLatest actually works as expected, so the problem must be somewhere in my test or in the TYPO3 setup.
So I started to debug my test and first of all, I verified, that the Testing Framework really created 3 records. Yes, 3 records were created, all having the
pid 0. Ok then, since the testrecords where created correctly, there seem to be something wrong with the search. I'll then debugged the
resulting SQL Query from the repository function findLatest and found out, that the records were searched in
pid 1. OK, so the problem seems to have something to do with the storagePids.
ExtBase, tests and storagePid(s)
When you create an ExtBase extension, the Extension Builder automatically creates some TS for you where you can set the storagePid(s) for your extension.
persistence {
# cat=plugin.tx_myext//a; type=string; label=Default storage PID
storagePid =
}
This
storagePid can be used to set the
location of your records in the TYPO3 backend. Generally every TYPO3 Extension you create with the Extension Builder makes use of the storagePid(s) and overrides them, if a startingPoint is set in the plugin.
When you run your tests in TYPO3 backend (or through your IDE), ExtBase uses the
backendConfigurationManager to get the ExtBase framework configuration. In the backendConfigurationManager class, there is a function called
getDefaultStoragePid(), which returns the local function getCurrentPageId(). Let's have a look at this function.
protected function getCurrentPageId() {
$pageId = (integer) \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id');
if ($pageId > 0) {
return $pageId;
}
// get current site root
$rootPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'deleted=0 AND hidden=0 AND is_siteroot=1', '', '', '1');
if (count($rootPages) > 0) {
return $rootPages[0]['uid'];
}
// get root template
$rootTemplates = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('pid', 'sys_template', 'deleted=0 AND hidden=0 AND root=1', '', '', '1');
if (count($rootTemplates) > 0) {
return $rootTemplates[0]['pid'];
}
// fallback
return self::DEFAULT_BACKEND_STORAGE_PID;
}
So, if your TYPO3 installation where you develop your ExtBase extension has a
root page, then this page ID is returned. Or if you have no root page but a
TS Template which is marked as
root template, then the page ID of that page is returned.
The returned page ID is used in the
queryFactory class to set the
storagePid(s), which leads to the situation I described earlier.
Solution / Workarounds
So how do we tell TYPO3 to search for our test records in
pid 0 during tests? On solution is to set the storagePid for the ExtBase Framework to 0 like shown below.
config.tx_extbase {
persistence {
storagePid = 0
}
}
This solution has some
problems, since it can be
overridden by other extensions and also it requires
manual configuration when setting up your testing environment.
You could also just
disable the check for
storagePid(s) (with setRespectStoragePage(FALSE)) in your tests, but I think this solution is'nt really good, since your tests then also may find records you created in the TYPO3 backend during development.
If you want to keep your tests independent, I would recommend to
set the storagePid(s) directly in the
setup of the tests.
/**
* Set up for test
*
* @return void
*/
public function setUp() {
$this->testingFramework = new \Tx_Phpunit_Framework('tx_myext');
$this->fixture = $this->objectManager->get('Derhansen\\MyExt\\Domain\\Repository\\MyRepository');
/** @var $querySettings Typo3QuerySettings */
$querySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings');
$querySettings->setStoragePageIds(array(0));
$this->fixture->setDefaultQuerySettings($querySettings);
}
The code above sets the
storage pid to 0 and now the tests should find the records created with the testing framework and tests should not interfere with other records created manually.