{"id":60,"date":"2026-10-16T07:00:00","date_gmt":"2026-10-16T07:00:00","guid":{"rendered":"https:\/\/myport.ddev.site\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/"},"modified":"2026-10-16T12:00:12","modified_gmt":"2026-10-16T12:00:12","slug":"functional-tests-for-an-extbase-repository-with-csv-fixtures","status":"publish","type":"post","link":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/","title":{"rendered":"Functional tests for an Extbase repository with CSV fixtures"},"content":{"rendered":"<p>Hi guys,<\/p>\n<p>Many TYPO3 extensions I open have no tests at all. When I ask why, the answer is nearly always the same: &#8220;the testing framework looks heavy&#8221;. I understand. The first time I saw <code>typo3\/testing-framework<\/code>, I was also like a snail, reading bootstrap files instead of writing a test.<\/p>\n<p>The truth is that one functional test for a repository needs four small files. This post shows them for an extension that runs on TYPO3 13.4 and 14.3, and how to run the suite inside DDEV.<\/p>\n<h2>The thing we want to test<\/h2>\n<p>Imagine an events extension with a custom repository method. It returns the next upcoming events, sorted by start date, and ignores the storage page because the plugin shows events from everywhere:<\/p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace MyVendorMyEventsDomainRepository;\n\nuse MyVendorMyEventsDomainModelEvent;\nuse TYPO3CMSExtbasePersistenceQueryInterface;\nuse TYPO3CMSExtbasePersistenceQueryResultInterface;\nuse TYPO3CMSExtbasePersistenceRepository;\n\n\/**\n * @extends Repository&lt;Event&gt;\n *\/\nfinal class EventRepository extends Repository\n{\n    \/**\n     * @return QueryResultInterface&lt;int, Event&gt;\n     *\/\n    public function findUpcoming(DateTimeInterface $now, int $limit = 3): QueryResultInterface\n    {\n        $query = $this-&gt;createQuery();\n        $query-&gt;getQuerySettings()-&gt;setRespectStoragePage(false);\n\n        return $query\n            -&gt;matching($query-&gt;greaterThanOrEqual('startDate', $now))\n            -&gt;setOrderings(['startDate' =&gt; QueryInterface::ORDER_ASCENDING])\n            -&gt;setLimit($limit)\n            -&gt;execute();\n    }\n}<\/code><\/pre>\n<p>The table is <code>tx_myevents_domain_model_event<\/code> with <code>title<\/code> and <code>start_date<\/code> (a datetime field stored as integer timestamp). A query like this is exactly where bugs hide: wrong operator, wrong sort direction, a forgotten storage page setting. A unit test cannot catch them, because the real work happens in the database.<\/p>\n<h2>Why not a unit test with mocks?<\/h2>\n<p>You could mock the query object and assert that <code>greaterThanOrEqual()<\/code> was called with <code>startDate<\/code>. That test would pass, and it would prove almost nothing. It tests that the code was written the way it was written. It does not test that Extbase converts the date to the right timestamp, that the column is mapped correctly, or that the sorting does what you think. A functional test boots a real TYPO3 instance with a real database, imports rows, and runs the real query. When it is green, the feature works.<\/p>\n<p>The price is speed: a functional test case needs a second or two to set up its instance. For repository code that is a good deal.<\/p>\n<h2>Where the files go<\/h2>\n<p>For the example the layout inside the extension looks like this. <code>Fixtures<\/code> holds the data to import, <code>Assertions<\/code> the expected database state after a write:<\/p>\n<pre><code class=\"language-text\">Tests\/Functional\/Domain\/Repository\/\n\u251c\u2500\u2500 EventRepositoryTest.php\n\u251c\u2500\u2500 Fixtures\/\n\u2502   \u2514\u2500\u2500 Events.csv\n\u2514\u2500\u2500 Assertions\/\n    \u2514\u2500\u2500 EventAdded.csv<\/code><\/pre>\n<h2>Step 1: Install the testing framework<\/h2>\n<p>Version 9 of the framework supports both TYPO3 13 and 14, with PHPUnit 11 or 12:<\/p>\n<pre><code class=\"language-bash\">ddev composer require --dev \"typo3\/testing-framework:^9\"\nmkdir -p Build\/phpunit\ncp vendor\/typo3\/testing-framework\/Resources\/Core\/Build\/FunctionalTests.xml Build\/phpunit\/\ncp vendor\/typo3\/testing-framework\/Resources\/Core\/Build\/FunctionalTestsBootstrap.php Build\/phpunit\/<\/code><\/pre>\n<p>The framework itself says to copy these two files instead of using them from <code>vendor\/<\/code>. In the copied XML, change the test suite directory to your extension, for example <code>..\/..\/Tests\/Functional\/<\/code>.<\/p>\n<h2>Step 2: The CSV fixture<\/h2>\n<p>CSV data sets are the easiest way to describe database rows. The first line names the table, the next line the columns (with a leading comma), then one line per record. One file can hold several tables.<\/p>\n<pre><code class=\"language-text\">\"pages\"\n,\"uid\",\"pid\",\"title\"\n,1,0,\"Root\"\n,5,1,\"Events\"\n\"tx_myevents_domain_model_event\"\n,\"uid\",\"pid\",\"title\",\"start_date\",\"hidden\",\"deleted\"\n,1,5,\"TYPO3 meetup\",1924984800,0,0\n,2,5,\"Old barcamp\",1577872800,0,0\n,3,5,\"Code sprint\",1893448800,0,0<\/code><\/pre>\n<p>Record 2 lies in the past and must not appear. Records 3 and 1 lie in the future, and 3 comes first. I like to use timestamps far away from today, so the test does not break next year.<\/p>\n<h2>Step 3: The test case<\/h2>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace MyVendorMyEventsTestsFunctionalDomainRepository;\n\nuse MyVendorMyEventsDomainModelEvent;\nuse MyVendorMyEventsDomainRepositoryEventRepository;\nuse PHPUnitFrameworkAttributesTest;\nuse TYPO3CMSCoreCoreSystemEnvironmentBuilder;\nuse TYPO3CMSCoreHttpServerRequest;\nuse TYPO3CMSExtbaseConfigurationConfigurationManagerInterface;\nuse TYPO3CMSExtbasePersistenceGenericPersistenceManager;\nuse TYPO3TestingFrameworkCoreFunctionalFunctionalTestCase;\n\nfinal class EventRepositoryTest extends FunctionalTestCase\n{\n    protected array $testExtensionsToLoad = [\n        'myvendor\/my-events',\n    ];\n\n    private EventRepository $subject;\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n        \/\/ Extbase reads its configuration from a request; give it a minimal one\n        $request = (new ServerRequest())\n            -&gt;withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_BE);\n        $this-&gt;get(ConfigurationManagerInterface::class)-&gt;setRequest($request);\n\n        $this-&gt;importCSVDataSet(__DIR__ . '\/Fixtures\/Events.csv');\n        $this-&gt;subject = $this-&gt;get(EventRepository::class);\n    }\n\n    #[Test]\n    public function findUpcomingReturnsFutureEventsSortedByStartDate(): void\n    {\n        $now = new DateTimeImmutable('@1735689600');\n\n        $titles = array_map(\n            static fn(Event $event): string =&gt; $event-&gt;getTitle(),\n            $this-&gt;subject-&gt;findUpcoming($now)-&gt;toArray()\n        );\n\n        self::assertSame(['Code sprint', 'TYPO3 meetup'], $titles);\n    }\n\n    #[Test]\n    public function addedEventIsWrittenToTheDatabase(): void\n    {\n        $event = new Event();\n        $event-&gt;setPid(5);\n        $event-&gt;setTitle('Release party');\n        $event-&gt;setStartDate(new DateTimeImmutable('@1956520800'));\n\n        $this-&gt;subject-&gt;add($event);\n        $this-&gt;get(PersistenceManager::class)-&gt;persistAll();\n\n        $this-&gt;assertCSVDataSet(__DIR__ . '\/Assertions\/EventAdded.csv');\n    }\n}<\/code><\/pre>\n<p>A few things are worth explaining:<\/p>\n<ul>\n<li><code>$testExtensionsToLoad<\/code> accepts the Composer package name when the extension is required in the root <code>composer.json<\/code>. Its TCA and database schema are set up in a fresh test instance.<\/li>\n<li><code>$this->get()<\/code> fetches services from the container, including non-public ones, so you test the real repository with real dependencies.<\/li>\n<li>The request in <code>setUp()<\/code> is the same pattern the core uses in its own Extbase tests. Without it, the configuration manager has no context to work with.<\/li>\n<li><code>setPid(5)<\/code> keeps the new record on the events folder instead of depending on a storage pid from TypoScript, which the test instance does not have.<\/li>\n<\/ul>\n<h2>Step 4: Assert the database state<\/h2>\n<p><code>assertCSVDataSet()<\/code> compares only the columns you list. That keeps the assertion readable:<\/p>\n<pre><code class=\"language-text\">\"tx_myevents_domain_model_event\"\n,\"uid\",\"pid\",\"title\",\"start_date\"\n,1,5,\"TYPO3 meetup\",1924984800\n,2,5,\"Old barcamp\",1577872800\n,3,5,\"Code sprint\",1893448800\n,4,5,\"Release party\",1956520800<\/code><\/pre>\n<p>If a row is missing or a value differs, PHPUnit prints a table with the difference. Much better than a <code>var_dump<\/code> in a controller.<\/p>\n<h2>Step 5: Run it in DDEV<\/h2>\n<p>The framework reads the database connection from environment variables. The quickest start is SQLite, if the <code>pdo_sqlite<\/code> extension is available in your web container:<\/p>\n<pre><code class=\"language-bash\">ddev exec typo3DatabaseDriver=pdo_sqlite \n    vendor\/bin\/phpunit -c Build\/phpunit\/FunctionalTests.xml<\/code><\/pre>\n<p>Before a release I also run against MariaDB, because that is what production uses. Each test case creates its own database with a suffix, so the user needs the right to create databases. In DDEV the <code>root<\/code> user can do that:<\/p>\n<pre><code class=\"language-bash\">ddev exec typo3DatabaseDriver=mysqli typo3DatabaseHost=db \n    typo3DatabaseUsername=root typo3DatabasePassword=root typo3DatabaseName=func \n    vendor\/bin\/phpunit -c Build\/phpunit\/FunctionalTests.xml<\/code><\/pre>\n<p>Put both commands into Composer scripts and you will never type them again.<\/p>\n<h2>Problems you will probably meet<\/h2>\n<ul>\n<li><strong>Empty results with no error.<\/strong> Extbase respects the storage page by default. Forget that, and the query quietly returns nothing. A test shows it at once. Hence the explicit <code>setRespectStoragePage(false)<\/code>.<\/li>\n<li><strong>Tests that depend on today.<\/strong> Passing <code>$now<\/code> into the method instead of calling <code>new DateTime()<\/code> inside makes it testable. Small design change, big win.<\/li>\n<li><strong>Column names in CSV.<\/strong> They are database column names (<code>start_date<\/code>), not Extbase property names (<code>startDate<\/code>). Mixing them up gives a confusing failure.<\/li>\n<\/ul>\n<p>Once the first test is green, the second one costs five minutes. That is the whole trick: get the first one running. Cheers!<\/p>\n<h2>Further reading<\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.typo3.org\/m\/typo3\/reference-coreapi\/main\/en-us\/Testing\/FunctionalTesting\/Index.html\">TYPO3: Functional testing<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/TYPO3\/testing-framework\">TYPO3 testing-framework on GitHub<\/a><\/li>\n<li><a href=\"https:\/\/docs.typo3.org\/m\/typo3\/reference-coreapi\/main\/en-us\/ExtensionArchitecture\/Extbase\/Reference\/Domain\/Repository.html\">Extbase repositories<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Setting up typo3\/testing-framework for a v13\/v14 extension: a FunctionalTestCase, CSV fixtures, a custom repository query and a database assertion, run inside DDEV.<\/p>\n","protected":false},"author":1,"featured_media":61,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[14,30,32,31,5],"class_list":["post-60","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-typo3","tag-extbase","tag-extension-development","tag-php-8","tag-testing","tag-typo3"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Functional tests for Extbase repositories in TYPO3<\/title>\n<meta name=\"description\" content=\"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Functional tests for Extbase repositories in TYPO3\" \/>\n<meta property=\"og:description\" content=\"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/\" \/>\n<meta property=\"og:site_name\" content=\"Hoja Mustaffa Abdul Latheef \u2013 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-16T07:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-16T12:00:12+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:site\" content=\"@hojaonline\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Hoja Mustaffa Abdul Latheef\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/\"},\"author\":{\"name\":\"Hoja Mustaffa Abdul Latheef\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"headline\":\"Functional tests for an Extbase repository with CSV fixtures\",\"datePublished\":\"2026-10-16T07:00:00+00:00\",\"dateModified\":\"2026-10-16T12:00:12+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/\"},\"wordCount\":773,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg\",\"keywords\":[\"extbase\",\"Extension development\",\"PHP 8\",\"Testing\",\"TYPO3\"],\"articleSection\":[\"TYPO3\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/\",\"name\":\"Functional tests for Extbase repositories in TYPO3\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg\",\"datePublished\":\"2026-10-16T07:00:00+00:00\",\"dateModified\":\"2026-10-16T12:00:12+00:00\",\"description\":\"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg\",\"contentUrl\":\"https:\\\/\\\/hoja.in\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Repository tests with CSV\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/functional-tests-for-an-extbase-repository-with-csv-fixtures\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Functional tests for an Extbase repository with CSV fixtures\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/hoja.in\\\/blog\\\/\",\"name\":\"Hoja Mustaffa Abdul Latheef \u2013 Blog\",\"description\":\"Notes from daily work with TYPO3, PHP and AI-assisted engineering\",\"publisher\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hoja.in\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/e6fbd54c2061674d8ea3e7861d858c7b\",\"name\":\"Hoja Mustaffa Abdul Latheef\",\"logo\":{\"@id\":\"https:\\\/\\\/hoja.in\\\/blog\\\/#\\\/schema\\\/person\\\/image\\\/\"},\"description\":\"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.\",\"sameAs\":[\"https:\\\/\\\/hoja.in\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Functional tests for Extbase repositories in TYPO3","description":"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/","og_locale":"en_US","og_type":"article","og_title":"Functional tests for Extbase repositories in TYPO3","og_description":"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.","og_url":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/","og_site_name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","article_published_time":"2026-10-16T07:00:00+00:00","article_modified_time":"2026-10-16T12:00:12+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg","type":"image\/jpeg"}],"author":"Hoja Mustaffa Abdul Latheef","twitter_card":"summary_large_image","twitter_creator":"@hojaonline","twitter_site":"@hojaonline","twitter_misc":{"Written by":"Hoja Mustaffa Abdul Latheef","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#article","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/"},"author":{"name":"Hoja Mustaffa Abdul Latheef","@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"headline":"Functional tests for an Extbase repository with CSV fixtures","datePublished":"2026-10-16T07:00:00+00:00","dateModified":"2026-10-16T12:00:12+00:00","mainEntityOfPage":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/"},"wordCount":773,"commentCount":0,"publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"image":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg","keywords":["extbase","Extension development","PHP 8","Testing","TYPO3"],"articleSection":["TYPO3"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/","url":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/","name":"Functional tests for Extbase repositories in TYPO3","isPartOf":{"@id":"https:\/\/hoja.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#primaryimage"},"image":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#primaryimage"},"thumbnailUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg","datePublished":"2026-10-16T07:00:00+00:00","dateModified":"2026-10-16T12:00:12+00:00","description":"Test a custom Extbase repository query in TYPO3 v13 and v14 with FunctionalTestCase, CSV data sets and assertCSVDataSet, and run the suite in DDEV.","breadcrumb":{"@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#primaryimage","url":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg","contentUrl":"https:\/\/hoja.in\/blog\/wp-content\/uploads\/2026\/10\/functional-tests-for-an-extbase-repository-with-csv-fixtures.jpg","width":1200,"height":630,"caption":"Repository tests with CSV"},{"@type":"BreadcrumbList","@id":"https:\/\/hoja.in\/blog\/functional-tests-for-an-extbase-repository-with-csv-fixtures\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/hoja.in\/blog\/"},{"@type":"ListItem","position":2,"name":"Functional tests for an Extbase repository with CSV fixtures"}]},{"@type":"WebSite","@id":"https:\/\/hoja.in\/blog\/#website","url":"https:\/\/hoja.in\/blog\/","name":"Hoja Mustaffa Abdul Latheef \u2013 Blog","description":"Notes from daily work with TYPO3, PHP and AI-assisted engineering","publisher":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hoja.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/e6fbd54c2061674d8ea3e7861d858c7b","name":"Hoja Mustaffa Abdul Latheef","logo":{"@id":"https:\/\/hoja.in\/blog\/#\/schema\/person\/image\/"},"description":"Senior TYPO3 Developer at jweiland.net, TYPO3 CMS Certified Developer. I write about TYPO3 upgrades, extension development, PHP and AI-assisted engineering with Claude Code.","sameAs":["https:\/\/hoja.in\/"]}]}},"_links":{"self":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/60","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/comments?post=60"}],"version-history":[{"count":1,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/60\/revisions"}],"predecessor-version":[{"id":153,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/posts\/60\/revisions\/153"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media\/61"}],"wp:attachment":[{"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/media?parent=60"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/categories?post=60"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hoja.in\/blog\/wp-json\/wp\/v2\/tags?post=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}