Skip to content

Commit f020edc

Browse files
authored
Merge pull request #552 from Maxcastel/add-dry-run-orm-executor
Add DryRunORMExecutor class for dry-run support
2 parents 8a45590 + f1b39a7 commit f020edc

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

docs/en/how-to/loading-fixtures.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,16 @@ To do so, you can use ``MultipleTransactionORMExecutor``.
124124
125125
<?php
126126
$executor = new MultipleTransactionORMExecutor($entityManager, new ORMPurger());
127+
128+
If you want to simulate the execution of fixtures without applying
129+
any changes to the database, you can use ``DryRunORMExecutor``.
130+
This executor will execute your fixtures, but instead of persisting and
131+
committing changes, it wraps the entire process in a transaction and
132+
rolls it back at the end.
133+
This allows you to inspect the behavior of your fixtures safely, without
134+
modifying the database:
135+
136+
.. code-block:: php
137+
138+
<?php
139+
$executor = new DryRunORMExecutor($entityManager, new ORMPurger());

src/Executor/DryRunORMExecutor.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Common\DataFixtures\Executor;
6+
7+
/**
8+
* This executor allows to execute (and indirectly, print) SQL statements without
9+
* actually committing them to the database
10+
*/
11+
final class DryRunORMExecutor extends AbstractExecutor
12+
{
13+
use ORMExecutorCommon;
14+
15+
/** @inheritDoc */
16+
public function execute(array $fixtures, bool $append = false): void
17+
{
18+
$executor = $this;
19+
$this->em->beginTransaction();
20+
try {
21+
if ($append === false) {
22+
$executor->purge();
23+
}
24+
25+
foreach ($fixtures as $fixture) {
26+
$executor->load($this->em, $fixture);
27+
}
28+
29+
$this->em->flush();
30+
} finally {
31+
$this->em->rollBack();
32+
$this->em->close();
33+
}
34+
}
35+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Common\DataFixtures\Executor;
6+
7+
use Doctrine\Common\DataFixtures\Executor\DryRunORMExecutor;
8+
use Doctrine\Common\DataFixtures\FixtureInterface;
9+
use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
10+
use Doctrine\ORM\EntityManager;
11+
use Doctrine\ORM\EntityManagerInterface;
12+
use Doctrine\Tests\Common\DataFixtures\BaseTestCase;
13+
use PHPUnit\Framework\MockObject\MockObject;
14+
use RuntimeException;
15+
16+
class DryRunORMExecutorTest extends BaseTestCase
17+
{
18+
public function testExecuteWithNoPurge(): void
19+
{
20+
$em = $this->getMockEntityManager();
21+
$em->expects($this->once())->method('beginTransaction');
22+
$em->expects($this->once())->method('flush');
23+
$em->expects($this->once())->method('rollBack');
24+
$em->expects($this->once())->method('close');
25+
26+
$purger = $this->getMockPurger();
27+
$purger->expects($this->never())
28+
->method('purge');
29+
30+
$executor = new DryRunORMExecutor($em, $purger);
31+
$fixture = $this->getMockFixture();
32+
$fixture->expects($this->once())
33+
->method('load')
34+
->with($em);
35+
$executor->execute([$fixture], true);
36+
}
37+
38+
public function testExecuteWithPurge(): void
39+
{
40+
$em = $this->getMockEntityManager();
41+
$em->expects($this->once())->method('beginTransaction');
42+
$em->expects($this->once())->method('flush');
43+
$em->expects($this->once())->method('rollBack');
44+
$em->expects($this->once())->method('close');
45+
46+
$purger = $this->getMockPurger();
47+
$purger->expects($this->once())
48+
->method('purge');
49+
50+
$executor = new DryRunORMExecutor($em, $purger);
51+
$fixture = $this->getMockFixture();
52+
$fixture->expects($this->once())
53+
->method('load')
54+
->with($em);
55+
$executor->execute([$fixture], false);
56+
}
57+
58+
public function testExecuteRollbackOnException(): void
59+
{
60+
$em = $this->getMockEntityManager();
61+
$em->expects($this->once())->method('beginTransaction');
62+
$em->expects($this->never())->method('flush');
63+
$em->expects($this->once())->method('rollBack');
64+
$em->expects($this->once())->method('close');
65+
66+
$purger = $this->getMockPurger();
67+
$purger->expects($this->never())
68+
->method('purge');
69+
70+
$executor = new DryRunORMExecutor($em, $purger);
71+
$fixture = $this->getMockFixture();
72+
$fixture->expects($this->once())
73+
->method('load')
74+
->willThrowException(new RuntimeException());
75+
76+
$this->expectException(RuntimeException::class);
77+
$executor->execute([$fixture], true);
78+
}
79+
80+
private function getMockEntityManager(): EntityManagerInterface&MockObject
81+
{
82+
$em = $this->getMockSqliteEntityManager();
83+
84+
return $this->getMockBuilder(EntityManager::class)
85+
->setConstructorArgs([
86+
$em->getConnection(),
87+
$em->getConfiguration(),
88+
$em->getEventManager(),
89+
])
90+
->onlyMethods(['beginTransaction', 'flush', 'rollBack', 'close'])
91+
->getMock();
92+
}
93+
94+
private function getMockFixture(): FixtureInterface&MockObject
95+
{
96+
return $this->createMock(FixtureInterface::class);
97+
}
98+
99+
private function getMockPurger(): ORMPurgerInterface&MockObject
100+
{
101+
return $this->createMock(ORMPurgerInterface::class);
102+
}
103+
}

0 commit comments

Comments
 (0)