From cd4e52653dda6f4b135f037dbb0daee3cda8c703 Mon Sep 17 00:00:00 2001
From: Christian Heel <66922325+heelc29@users.noreply.github.com>
Date: Mon, 6 Jul 2026 18:08:59 +0200
Subject: [PATCH 1/2] missing driver independent tests
---
.github/workflows/ci.yml | 16 ++++++++++++++++
phpunit.xml.dist | 1 -
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 51fca5c2..84674b4d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -60,6 +60,22 @@ jobs:
run: |
./vendor/bin/phpstan --error-format=github
+ tests-unit:
+ name: Run Unit tests
+ runs-on: ubuntu-latest
+ container: joomlaprojects/docker-images:php${{ matrix.php_version }}
+ needs: [code-style-php]
+ strategy:
+ matrix:
+ php_version: ['8.3', '8.4', '8.5']
+ steps:
+ - uses: actions/checkout@v6
+ - name: Run Unit tests
+ run: |
+ git config --global --add safe.directory $GITHUB_WORKSPACE
+ composer update
+ ./vendor/bin/phpunit
+
tests-unit-sqlite:
name: Run Unit tests on SQLite
runs-on: ubuntu-latest
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index a4fd6ec2..c880c9cb 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -17,7 +17,6 @@
Tests/Monitor
Tests/Query
Tests/Service
- Tests/AbstractDatabaseDriverTestCase.php
Tests/DatabaseAwareTraitTest.php
Tests/DatabaseExporterTest.php
Tests/DatabaseFactoryTest.php
From a1a46040fcd46ee5d2ae36bc0a8aac84bf88ebb4 Mon Sep 17 00:00:00 2001
From: Christian Heel <66922325+heelc29@users.noreply.github.com>
Date: Mon, 6 Jul 2026 18:09:42 +0200
Subject: [PATCH 2/2] make tests/command db driver independent
---
Tests/Command/ExportCommandTest.php | 189 +++++++++++++++++-----------
Tests/Command/ImportCommandTest.php | 99 +++++++++++----
phpunit.xml.dist | 10 +-
3 files changed, 198 insertions(+), 100 deletions(-)
diff --git a/Tests/Command/ExportCommandTest.php b/Tests/Command/ExportCommandTest.php
index 93a48bba..358139d0 100644
--- a/Tests/Command/ExportCommandTest.php
+++ b/Tests/Command/ExportCommandTest.php
@@ -10,17 +10,17 @@
use Joomla\Console\Application;
use Joomla\Database\Command\ExportCommand;
use Joomla\Database\DatabaseDriver;
-use Joomla\Database\Exception\ExecutionFailureException;
+use Joomla\Database\DatabaseExporter;
use Joomla\Database\Exception\UnsupportedAdapterException;
use Joomla\Filesystem\Folder;
-use Joomla\Test\DatabaseTestCase;
+use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* Test class for Joomla\Database\Command\ExportCommand
*/
-class ExportCommandTest extends DatabaseTestCase
+class ExportCommandTest extends TestCase
{
/**
* Path to the test space
@@ -48,10 +48,6 @@ public static function setUpBeforeClass(): void
}
parent::setUpBeforeClass();
-
- if (!static::$connection || static::$connection->getName() !== 'mysql') {
- self::markTestSkipped('MySQL database not configured.');
- }
}
/**
@@ -64,20 +60,6 @@ protected function setUp(): void
{
parent::setUp();
- try {
- foreach (DatabaseDriver::splitSql(file_get_contents(dirname(__DIR__) . '/Stubs/Schema/mysql.sql')) as $query) {
- static::$connection->setQuery($query)
- ->execute();
- }
- } catch (ExecutionFailureException $exception) {
- $this->markTestSkipped(
- \sprintf(
- 'Could not load MySQL database: %s',
- $exception->getMessage()
- )
- );
- }
-
$this->umask = umask(0);
$this->testPath = sys_get_temp_dir() . '/' . microtime(true) . '.' . mt_rand();
@@ -98,55 +80,35 @@ protected function tearDown(): void
umask($this->umask);
- foreach (static::$connection->getTableList() as $table) {
- static::$connection->dropTable($table);
- }
-
parent::tearDown();
}
- /**
- * Loads the example data into the database.
- *
- * @return void
- */
- protected function loadExampleData(): void
- {
- $data = [
- (object) [
- 'id' => 1,
- 'title' => 'Testing1',
- 'start_date' => '2019-10-26 00:00:00',
- 'description' => 'test row one',
- ],
- (object) [
- 'id' => 2,
- 'title' => 'Testing2',
- 'start_date' => '2019-10-26 00:00:00',
- 'description' => 'test row two',
- ],
- (object) [
- 'id' => 3,
- 'title' => 'Testing3',
- 'start_date' => '2019-10-26 00:00:00',
- 'description' => 'test row three',
- ],
- (object) [
- 'id' => 4,
- 'title' => 'Testing4',
- 'start_date' => '2019-10-26 00:00:00',
- 'description' => 'test row four',
- ],
- ];
-
- foreach ($data as $row) {
- static::$connection->insertObject('#__dbtest', $row);
- }
- }
-
public function testTheDatabaseIsExportedWithAllTables()
{
- $this->loadExampleData();
+ $db = $this->createMock(DatabaseDriver::class);
+ $exporter = $this->createMock(DatabaseExporter::class);
+
+ $exporter->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->exactly(2))
+ ->method('withData')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->exactly(2))
+ ->method('from')
+ ->willReturnSelf();
+
+ $db->expects($this->once())
+ ->method('getExporter')
+ ->willReturn($exporter);
+ $db->expects($this->once())
+ ->method('getTableList')
+ ->willReturn(['dbtest', 'dbtest2']);
+ $db->expects($this->once())
+ ->method('getPrefix')
+ ->willReturn('');
$input = new ArrayInput(
[
@@ -158,18 +120,47 @@ public function testTheDatabaseIsExportedWithAllTables()
$application = new Application($input, $output);
- $command = new ExportCommand(static::$connection);
+ $command = new ExportCommand($db);
$command->setApplication($application);
$this->assertSame(0, $command->execute($input, $output));
+ $this->assertTrue(is_file($this->testPath . '/dbtest.xml'));
+ $this->assertTrue(is_file($this->testPath . '/dbtest2.xml'));
$screenOutput = $output->fetch();
+ $this->assertStringContainsString('Processing the dbtest table', $screenOutput);
+ $this->assertStringContainsString('Exported data for dbtest in', $screenOutput);
+ $this->assertStringContainsString('Processing the dbtest2 table', $screenOutput);
+ $this->assertStringContainsString('Exported data for dbtest2 in', $screenOutput);
$this->assertStringContainsString('Export completed in', $screenOutput);
}
public function testTheDatabaseIsExportedWithAllTablesInZipFormat()
{
- $this->loadExampleData();
+ $db = $this->createMock(DatabaseDriver::class);
+ $exporter = $this->createMock(DatabaseExporter::class);
+
+ $exporter->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->exactly(2))
+ ->method('withData')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->exactly(2))
+ ->method('from')
+ ->willReturnSelf();
+
+ $db->expects($this->once())
+ ->method('getExporter')
+ ->willReturn($exporter);
+ $db->expects($this->once())
+ ->method('getTableList')
+ ->willReturn(['dbtest', 'dbtest2']);
+ $db->expects($this->once())
+ ->method('getPrefix')
+ ->willReturn('');
$input = new ArrayInput(
[
@@ -182,18 +173,46 @@ public function testTheDatabaseIsExportedWithAllTablesInZipFormat()
$application = new Application($input, $output);
- $command = new ExportCommand(static::$connection);
+ $command = new ExportCommand($db);
$command->setApplication($application);
$this->assertSame(0, $command->execute($input, $output));
+ $this->assertCount(1, glob($this->testPath . '/data_exported_*.zip'));
$screenOutput = $output->fetch();
+ $this->assertStringContainsString('Processing the dbtest table', $screenOutput);
+ $this->assertStringContainsString('Exported data for dbtest in', $screenOutput);
+ $this->assertStringContainsString('Processing the dbtest2 table', $screenOutput);
+ $this->assertStringContainsString('Exported data for dbtest2 in', $screenOutput);
$this->assertStringContainsString('Export completed in', $screenOutput);
}
public function testTheDatabaseIsExportedWithASingleTable()
{
- $this->loadExampleData();
+ $db = $this->createMock(DatabaseDriver::class);
+ $exporter = $this->createMock(DatabaseExporter::class);
+
+ $exporter->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->once())
+ ->method('withData')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->once())
+ ->method('from')
+ ->willReturnSelf();
+
+ $db->expects($this->once())
+ ->method('getExporter')
+ ->willReturn($exporter);
+ $db->expects($this->once())
+ ->method('getTableList')
+ ->willReturn(['dbtest', 'dbtest2']);
+ $db->expects($this->once())
+ ->method('getPrefix')
+ ->willReturn('');
$input = new ArrayInput(
[
@@ -206,12 +225,16 @@ public function testTheDatabaseIsExportedWithASingleTable()
$application = new Application($input, $output);
- $command = new ExportCommand(static::$connection);
+ $command = new ExportCommand($db);
$command->setApplication($application);
$this->assertSame(0, $command->execute($input, $output));
+ $this->assertTrue(is_file($this->testPath . '/dbtest.xml'));
+ $this->assertFalse(is_file($this->testPath . '/dbtest2.xml'));
$screenOutput = $output->fetch();
+ $this->assertStringContainsString('Processing the dbtest table', $screenOutput);
+ $this->assertStringContainsString('Exported data for dbtest in', $screenOutput);
$this->assertStringContainsString('Export completed in', $screenOutput);
}
@@ -247,7 +270,29 @@ public function testTheCommandFailsIfTheDatabaseDriverDoesNotSupportExports()
public function testTheCommandFailsIfTheRequestedTableDoesNotExistInTheDatabase()
{
- $this->loadExampleData();
+ $db = $this->createMock(DatabaseDriver::class);
+ $exporter = $this->createMock(DatabaseExporter::class);
+
+ $exporter->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $exporter->expects($this->never())
+ ->method('withData')
+ ->willReturnSelf();
+ $exporter->expects($this->never())
+ ->method('from')
+ ->willReturnSelf();
+
+ $db->expects($this->once())
+ ->method('getExporter')
+ ->willReturn($exporter);
+ $db->expects($this->once())
+ ->method('getTableList')
+ ->willReturn(['dbtest']);
+ $db->expects($this->once())
+ ->method('getPrefix')
+ ->willReturn('');
$input = new ArrayInput(
[
@@ -260,7 +305,7 @@ public function testTheCommandFailsIfTheRequestedTableDoesNotExistInTheDatabase(
$application = new Application($input, $output);
- $command = new ExportCommand(static::$connection);
+ $command = new ExportCommand($db);
$command->setApplication($application);
$this->assertSame(1, $command->execute($input, $output));
diff --git a/Tests/Command/ImportCommandTest.php b/Tests/Command/ImportCommandTest.php
index 109a5cf0..97135f95 100644
--- a/Tests/Command/ImportCommandTest.php
+++ b/Tests/Command/ImportCommandTest.php
@@ -10,15 +10,16 @@
use Joomla\Console\Application;
use Joomla\Database\Command\ImportCommand;
use Joomla\Database\DatabaseDriver;
+use Joomla\Database\DatabaseImporter;
use Joomla\Database\Exception\UnsupportedAdapterException;
-use Joomla\Test\DatabaseTestCase;
+use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* Test class for Joomla\Database\Command\ImportCommand
*/
-class ImportCommandTest extends DatabaseTestCase
+class ImportCommandTest extends TestCase
{
/**
* Path to the database stubs
@@ -39,10 +40,6 @@ public static function setUpBeforeClass(): void
}
parent::setUpBeforeClass();
-
- if (!static::$connection || static::$connection->getName() !== 'mysql') {
- self::markTestSkipped('MySQL database not configured.');
- }
}
/**
@@ -58,23 +55,30 @@ protected function setUp(): void
$this->stubPath = dirname(__DIR__) . '/Stubs/Importer';
}
- /**
- * Tears down the fixture, for example, close a network connection.
- * This method is called after a test is executed.
- *
- * @return void
- */
- protected function tearDown(): void
+ public function testTheDatabaseIsImportedWithAllTables()
{
- foreach (static::$connection->getTableList() as $table) {
- static::$connection->dropTable($table);
- }
+ $db = $this->createMock(DatabaseDriver::class);
+ $importer = $this->createMock(DatabaseImporter::class);
+
+ $importer->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $importer->expects($this->once())
+ ->method('asXml')
+ ->willReturnSelf();
+ $importer->expects($this->once())
+ ->method('mergeStructure');
+ $importer->expects($this->once())
+ ->method('importData');
- parent::tearDown();
- }
+ $db->expects($this->once())
+ ->method('getImporter')
+ ->willReturn($importer);
+ $db->expects($this->once())
+ ->method('dropTable')
+ ->with('dbtest', true);
- public function testTheDatabaseIsImportedWithAllTables()
- {
$input = new ArrayInput(
[
'command' => 'database:import',
@@ -85,17 +89,42 @@ public function testTheDatabaseIsImportedWithAllTables()
$application = new Application($input, $output);
- $command = new ImportCommand(static::$connection);
+ $command = new ImportCommand($db);
$command->setApplication($application);
$this->assertSame(0, $command->execute($input, $output));
$screenOutput = $output->fetch();
+ $this->assertStringContainsString('Importing dbtest from dbtest.xml', $screenOutput);
+ $this->assertStringContainsString('Processing the dbtest table', $screenOutput);
+ $this->assertStringContainsString('Imported data for dbtest.xml in', $screenOutput);
$this->assertStringContainsString('Import completed in', $screenOutput);
}
public function testTheDatabaseIsImportedWithASingleTable()
{
+ $db = $this->createMock(DatabaseDriver::class);
+ $importer = $this->createMock(DatabaseImporter::class);
+
+ $importer->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $importer->expects($this->once())
+ ->method('asXml')
+ ->willReturnSelf();
+ $importer->expects($this->once())
+ ->method('mergeStructure');
+ $importer->expects($this->once())
+ ->method('importData');
+
+ $db->expects($this->once())
+ ->method('getImporter')
+ ->willReturn($importer);
+ $db->expects($this->once())
+ ->method('dropTable')
+ ->with('dbtest', true);
+
$input = new ArrayInput(
[
'command' => 'database:import',
@@ -107,12 +136,15 @@ public function testTheDatabaseIsImportedWithASingleTable()
$application = new Application($input, $output);
- $command = new ImportCommand(static::$connection);
+ $command = new ImportCommand($db);
$command->setApplication($application);
$this->assertSame(0, $command->execute($input, $output));
$screenOutput = $output->fetch();
+ $this->assertStringContainsString('Importing dbtest from dbtest.xml', $screenOutput);
+ $this->assertStringContainsString('Processing the dbtest table', $screenOutput);
+ $this->assertStringContainsString('Imported data for dbtest.xml in', $screenOutput);
$this->assertStringContainsString('Import completed in', $screenOutput);
}
@@ -148,6 +180,27 @@ public function testTheCommandFailsIfTheDatabaseDriverDoesNotSupportImports()
public function testTheCommandFailsIfTheRequestedTableDoesNotHaveAnImportFile()
{
+ $db = $this->createMock(DatabaseDriver::class);
+ $importer = $this->createMock(DatabaseImporter::class);
+
+ $importer->expects($this->once())
+ ->method('withStructure')
+ ->with(true)
+ ->willReturnSelf();
+ $importer->expects($this->once())
+ ->method('asXml')
+ ->willReturnSelf();
+ $importer->expects($this->never())
+ ->method('mergeStructure');
+ $importer->expects($this->never())
+ ->method('importData');
+
+ $db->expects($this->once())
+ ->method('getImporter')
+ ->willReturn($importer);
+ $db->expects($this->never())
+ ->method('dropTable');
+
$input = new ArrayInput(
[
'command' => 'database:import',
@@ -159,7 +212,7 @@ public function testTheCommandFailsIfTheRequestedTableDoesNotHaveAnImportFile()
$application = new Application($input, $output);
- $command = new ImportCommand(static::$connection);
+ $command = new ImportCommand($db);
$command->setApplication($application);
$this->assertSame(1, $command->execute($input, $output));
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c880c9cb..7f59d1b6 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,12 +1,12 @@
-
-
-
-
+
+
+
+
-
+