Skip to content

Commit bdd00d8

Browse files
committed
feat: Support switching between Dama and normal Mysql Schema Strategy in a test run
- store dirty/clean state of database between execution of test methods - store connection and schema strategy to run cleanup on the same objects
1 parent 65985a6 commit bdd00d8

File tree

3 files changed

+209
-73
lines changed

3 files changed

+209
-73
lines changed

phpstan.neon.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ parameters:
1515
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\.*Test::assertMatches.*Snapshot\(\) is protected, but since the containing class is final, it can be private.#'
1616
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\.*Test::setUpSnapshot\(\) is protected, but since the containing class is final, it can be private.#'
1717
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\Request\\RequestTraitTest::.*\(\) is protected, but since the containing class is final, it can be private#'
18+
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\Schema\\SchemaTraitTest::.*\(\) is protected, but since the containing class is final, it can be private#'
1819
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\Uuid\\UuidTraitTest::.*\(\) is protected, but since the containing class is final, it can be private#'
1920
- '#Method Brainbits\\FunctionalTestHelpers\\Tests\\ZipContents\\ZipContentsTraitTest::.*\(\) is protected, but since the containing class is final, it can be private#'
2021
- '#Safe\\DateTimeImmutable#'

src/Schema/SchemaTrait.php

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,53 @@
44

55
namespace Brainbits\FunctionalTestHelpers\Schema;
66

7+
use Brainbits\FunctionalTestHelpers\Schema\Strategy\SchemaStrategy;
78
use Doctrine\DBAL\Connection;
89
use Doctrine\DBAL\DriverManager;
910
use PHPUnit\Framework\TestCase;
1011

12+
use function assert;
1113
use function getenv;
1214

1315
// phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
1416

1517
/** @mixin TestCase */
1618
trait SchemaTrait
1719
{
18-
final protected function fixtureFromConnection( // @phpstan-ignore-line
20+
protected static bool $isSchemaDatabaseClean = false;
21+
22+
private SchemaStrategy|null $schemaStrategy = null;
23+
private Connection|null $schemaStrategyConnection = null;
24+
25+
final protected function fixtureFromConnection(
1926
Connection $connection,
2027
SchemaBuilder $schemaBuilder,
2128
DataBuilder $dataBuilder,
2229
callable $buildData,
2330
): void {
2431
$buildData($dataBuilder);
2532

26-
$schemaStrategy = (new CreateSchemaStrategy())($connection);
33+
$this->schemaStrategy = $this->createSchemaStrategy($connection);
2734

2835
if (!getenv('USE_PRE_INITIALIZED_SCHEMA')) {
29-
$schemaStrategy->applySchema($schemaBuilder, $connection);
36+
$this->schemaStrategy->applySchema($schemaBuilder, $connection);
37+
}
38+
39+
if (!self::$isSchemaDatabaseClean) {
40+
$this->schemaStrategy->deleteData($connection);
41+
$this->schemaStrategy->resetSequences($connection);
42+
}
43+
44+
try {
45+
$this->schemaStrategy->applyData($dataBuilder, $connection);
46+
} finally {
47+
self::$isSchemaDatabaseClean = false;
3048
}
3149

32-
$schemaStrategy->deleteData($connection);
33-
$schemaStrategy->resetSequences($connection);
34-
$schemaStrategy->applyData($dataBuilder, $connection);
50+
$this->schemaStrategyConnection = $connection;
3551
}
3652

37-
final protected function fixtureFromNewConnection( // @phpstan-ignore-line
53+
final protected function fixtureFromNewConnection(
3854
SchemaBuilder $schemaBuilder,
3955
DataBuilder $dataBuilder,
4056
callable $buildData,
@@ -50,4 +66,19 @@ final protected function fixtureFromNewConnection( // @phpstan-ignore-line
5066

5167
return $connection;
5268
}
69+
70+
final protected function cleanupFixture(): void
71+
{
72+
assert($this->schemaStrategyConnection !== null);
73+
74+
$this->schemaStrategy?->deleteData($this->schemaStrategyConnection);
75+
$this->schemaStrategy?->resetSequences($this->schemaStrategyConnection);
76+
77+
self::$isSchemaDatabaseClean = true;
78+
}
79+
80+
protected function createSchemaStrategy(Connection $connection): SchemaStrategy
81+
{
82+
return (new CreateSchemaStrategy())($connection);
83+
}
5384
}

tests/Schema/SchemaTraitTest.php

Lines changed: 170 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
use Brainbits\FunctionalTestHelpers\Schema\DataBuilder;
88
use Brainbits\FunctionalTestHelpers\Schema\SchemaBuilder;
99
use Brainbits\FunctionalTestHelpers\Schema\SchemaTrait;
10+
use Brainbits\FunctionalTestHelpers\Schema\Strategy\SchemaStrategy;
1011
use Doctrine\DBAL\Connection;
11-
use Doctrine\DBAL\Platforms\SqlitePlatform;
1212
use Doctrine\DBAL\Schema\Schema;
13+
use PHPUnit\Framework\MockObject\MockObject;
1314
use PHPUnit\Framework\TestCase;
1415

1516
use function getenv;
@@ -23,75 +24,187 @@ final class SchemaTraitTest extends TestCase
2324

2425
private string $oldEnvUsePreInitializedSchema;
2526

27+
private MockObject&Connection $connectionMock;
28+
private MockObject&SchemaStrategy $schemaStrategyMock;
29+
30+
private SchemaBuilder $schemaBuilder;
31+
private DataBuilder $dataBuilder;
32+
2633
protected function setUp(): void
2734
{
2835
$this->oldEnvUsePreInitializedSchema = (string) getenv('USE_PRE_INITIALIZED_SCHEMA');
36+
37+
self::$isSchemaDatabaseClean = false;
38+
39+
$this->connectionMock = $this->createMock(Connection::class);
40+
$this->schemaStrategyMock = $this->createMock(SchemaStrategy::class);
41+
42+
$this->schemaBuilder = $this->createSchemaBuilder();
43+
$this->dataBuilder = $this->createDataBuilder();
2944
}
3045

3146
protected function tearDown(): void
3247
{
3348
putenv(sprintf('USE_PRE_INITIALIZED_SCHEMA=%s', $this->oldEnvUsePreInitializedSchema));
3449
}
3550

36-
public function testFixtureFromConnectionWithTableNameQuote(): void
51+
public function testFixtureFromNewConnectionExecutesBuildDataCallback(): void
52+
{
53+
$callbackHasBeenCalled = false;
54+
55+
$this->fixtureFromNewConnection(
56+
$this->schemaBuilder,
57+
$this->dataBuilder,
58+
function ($dataBuilderInCallback) use (&$callbackHasBeenCalled): void {
59+
self::assertSame($this->dataBuilder, $dataBuilderInCallback);
60+
61+
$callbackHasBeenCalled = true;
62+
},
63+
);
64+
65+
self::assertTrue($callbackHasBeenCalled);
66+
}
67+
68+
public function testFixtureFromNewConnectionAppliesSchemaAndData(): void
69+
{
70+
$this->schemaStrategyMock->expects($this->once())
71+
->method('applySchema')
72+
->with($this->schemaBuilder, $this->isInstanceOf(Connection::class));
73+
74+
$this->schemaStrategyMock->expects($this->once())
75+
->method('applyData')
76+
->with($this->dataBuilder, $this->isInstanceOf(Connection::class));
77+
78+
$this->fixtureFromNewConnection(
79+
$this->schemaBuilder,
80+
$this->dataBuilder,
81+
static fn () => null,
82+
);
83+
}
84+
85+
public function testFixtureFromConnectionExecutesBuildDataCallback(): void
3786
{
38-
$schemaBuilder = $this->createSchemaBuilder();
39-
$dataBuilder = $this->createDataBuilder($schemaBuilder);
40-
41-
$connection = $this->createMock(Connection::class);
42-
$connection->expects($this->any())
43-
->method('getParams')
44-
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
45-
$connection->expects($this->any())
46-
->method('getDatabasePlatform')
47-
->willReturn(new SqlitePlatform());
48-
$connection->expects($this->once())
49-
->method('executeStatement')
50-
->with('CREATE TABLE foo (bar VARCHAR(255) NOT NULL)');
51-
$connection->expects($this->once())
52-
->method('insert')
53-
->with('"foo"', ['"bar"' => 'baz']);
87+
$callbackHasBeenCalled = false;
5488

5589
$this->fixtureFromConnection(
56-
$connection,
57-
$schemaBuilder,
58-
$dataBuilder,
59-
static function ($dataBuilder): void {
60-
$dataBuilder->foo('baz');
90+
$this->connectionMock,
91+
$this->schemaBuilder,
92+
$this->dataBuilder,
93+
function ($dataBuilderInCallback) use (&$callbackHasBeenCalled): void {
94+
self::assertSame($this->dataBuilder, $dataBuilderInCallback);
95+
96+
$callbackHasBeenCalled = true;
6197
},
6298
);
99+
100+
self::assertTrue($callbackHasBeenCalled);
101+
}
102+
103+
public function testFixtureFromConnectionAppliesSchemaAndData(): void
104+
{
105+
$this->schemaStrategyMock->expects($this->once())
106+
->method('applySchema')
107+
->with($this->schemaBuilder, $this->connectionMock);
108+
109+
$this->schemaStrategyMock->expects($this->once())
110+
->method('applyData')
111+
->with($this->dataBuilder, $this->connectionMock);
112+
113+
$this->fixtureFromConnection(
114+
$this->connectionMock,
115+
$this->schemaBuilder,
116+
$this->dataBuilder,
117+
static fn () => null,
118+
);
63119
}
64120

65121
public function testFixtureFromConnectionWithPreInitializedSchema(): void
66122
{
67123
putenv('USE_PRE_INITIALIZED_SCHEMA=1');
68124

69-
$schemaBuilder = $this->createSchemaBuilder();
70-
$dataBuilder = $this->createDataBuilder($schemaBuilder);
71-
72-
$connection = $this->createMock(Connection::class);
73-
$connection->expects($this->any())
74-
->method('getParams')
75-
->willReturn(['driver' => 'pdo_sqlite', 'memory' => true]);
76-
$connection->expects($this->any())
77-
->method('getDatabasePlatform')
78-
->willReturn(new SqlitePlatform());
79-
$connection->expects($this->never())
80-
->method('executeStatement');
81-
$connection->expects($this->once())
82-
->method('insert')
83-
->with('"foo"', ['"bar"' => 'baz']);
125+
$this->schemaStrategyMock->expects($this->never())
126+
->method('applySchema');
127+
128+
$this->schemaStrategyMock->expects($this->once())
129+
->method('applyData')
130+
->with($this->dataBuilder, $this->connectionMock);
84131

85132
$this->fixtureFromConnection(
86-
$connection,
87-
$schemaBuilder,
88-
$dataBuilder,
89-
static function ($dataBuilder): void {
90-
$dataBuilder->foo('baz');
91-
},
133+
$this->connectionMock,
134+
$this->schemaBuilder,
135+
$this->dataBuilder,
136+
static fn () => null,
137+
);
138+
}
139+
140+
public function testSchemaIsCleanedUpBeforeApplyingDataIfDatabaseIsDirty(): void
141+
{
142+
self::$isSchemaDatabaseClean = false;
143+
144+
$this->schemaStrategyMock->expects($this->once())
145+
->method('deleteData')
146+
->with($this->connectionMock);
147+
148+
$this->schemaStrategyMock->expects($this->once())
149+
->method('resetSequences')
150+
->with($this->connectionMock);
151+
152+
$this->fixtureFromConnection(
153+
$this->connectionMock,
154+
$this->schemaBuilder,
155+
$this->dataBuilder,
156+
static fn () => null,
157+
);
158+
}
159+
160+
public function testSchemaIsNotCleanedUpBeforeApplyingDataIfDatabaseIsClean(): void
161+
{
162+
self::$isSchemaDatabaseClean = true;
163+
164+
$this->schemaStrategyMock->expects($this->never())
165+
->method('deleteData');
166+
167+
$this->schemaStrategyMock->expects($this->never())
168+
->method('resetSequences');
169+
170+
$this->fixtureFromConnection(
171+
$this->connectionMock,
172+
$this->schemaBuilder,
173+
$this->dataBuilder,
174+
static fn () => null,
92175
);
93176
}
94177

178+
public function testDatabaseIsMarkedDirtyIfDataWasApplied(): void
179+
{
180+
self::$isSchemaDatabaseClean = true;
181+
182+
$this->fixtureFromConnection(
183+
$this->connectionMock,
184+
$this->schemaBuilder,
185+
$this->dataBuilder,
186+
static fn () => null,
187+
);
188+
189+
self::assertFalse(self::$isSchemaDatabaseClean);
190+
}
191+
192+
public function testDatabaseIsMarkedCleanAfterFixupIsCleanedUp(): void
193+
{
194+
self::$isSchemaDatabaseClean = false;
195+
196+
$this->fixtureFromConnection(
197+
$this->connectionMock,
198+
$this->schemaBuilder,
199+
$this->dataBuilder,
200+
static fn () => null,
201+
);
202+
203+
$this->cleanupFixture();
204+
205+
self::assertTrue(self::$isSchemaDatabaseClean);
206+
}
207+
95208
private function createSchemaBuilder(): SchemaBuilder
96209
{
97210
return new class implements SchemaBuilder {
@@ -111,46 +224,37 @@ public function getSchema(): Schema
111224
{
112225
return $this->schema;
113226
}
114-
115-
public function foo(): void
116-
{
117-
$table = $this->schema->createTable('foo');
118-
$table->addColumn('bar', 'string');
119-
}
120227
};
121228
}
122229

123-
private function createDataBuilder(SchemaBuilder|null $schemaBuilder = null): DataBuilder
230+
private function createDataBuilder(): DataBuilder
124231
{
125-
if (!$schemaBuilder) {
126-
$schemaBuilder = $this->createSchemaBuilder();
127-
}
128-
129-
return new class ($schemaBuilder) implements DataBuilder {
232+
return new class () implements DataBuilder {
130233
/** @var mixed[] */
131234
private array $data = [];
132235

133-
public function __construct(private SchemaBuilder $schemaBuilder)
236+
public function __construct()
134237
{
135238
}
136239

137-
public static function create(SchemaBuilder $schemaBuilder): DataBuilder
240+
public static function create(): DataBuilder
138241
{
139-
return new self($schemaBuilder);
242+
return new self();
140243
}
141244

142245
/** @return mixed[] */
143246
public function getData(): array
144247
{
145248
return $this->data;
146249
}
147-
148-
public function foo(string $bar): void
149-
{
150-
$this->schemaBuilder->foo();
151-
152-
$this->data['foo'][] = ['bar' => $bar];
153-
}
154250
};
155251
}
252+
253+
/**
254+
* Overridden this method to use a schema strategy mock.
255+
*/
256+
protected function createSchemaStrategy(Connection $connection): SchemaStrategy
257+
{
258+
return $this->schemaStrategyMock;
259+
}
156260
}

0 commit comments

Comments
 (0)