Skip to content

Commit 36b6991

Browse files
authored
[11.x] use exists() instead of count() (#53328)
* use `exists()` instead of `count()` we only need to check for existence of a record here, not for the actual count, and `exists()` is generally more performant than a count. * fix tests - add an "exists" assertions, and update `mockCountBuilder()` to accept this first, and adjust all the calls to it. - I thought about renaming `mockCountBuilder()` to `mockBuilder()`, which would probably be a little clearer now, but I wanted to keep this easy to review. if this is accepted, I'd probably go back and make that semantic change separately.
1 parent 55f435b commit 36b6991

File tree

4 files changed

+44
-37
lines changed

4 files changed

+44
-37
lines changed

src/Illuminate/Testing/Constraints/HasInDatabase.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public function __construct(Connection $database, array $data)
5151
*/
5252
public function matches($table): bool
5353
{
54-
return $this->database->table($table)->where($this->data)->count() > 0;
54+
return $this->database->table($table)
55+
->where($this->data)
56+
->exists();
5557
}
5658

5759
/**

src/Illuminate/Testing/Constraints/NotSoftDeletedInDatabase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function matches($table): bool
6161
return $this->database->table($table)
6262
->where($this->data)
6363
->whereNull($this->deletedAtColumn)
64-
->count() > 0;
64+
->exists();
6565
}
6666

6767
/**

src/Illuminate/Testing/Constraints/SoftDeletedInDatabase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function matches($table): bool
6363
return $this->database->table($table)
6464
->where($this->data)
6565
->whereNotNull($this->deletedAtColumn)
66-
->count() > 0;
66+
->exists();
6767
}
6868

6969
/**

tests/Foundation/FoundationInteractsWithDatabaseTest.php

+39-34
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Database\Query\Builder;
99
use Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase;
1010
use Illuminate\Foundation\Testing\TestCase as TestingTestCase;
11+
use Illuminate\Support\Arr;
1112
use Illuminate\Support\Facades\DB;
1213
use Mockery as m;
1314
use Orchestra\Testbench\Concerns\CreatesApplication;
@@ -39,14 +40,14 @@ protected function tearDown(): void
3940

4041
public function testSeeInDatabaseFindsResults()
4142
{
42-
$this->mockCountBuilder(1);
43+
$this->mockCountBuilder(true);
4344

4445
$this->assertDatabaseHas($this->table, $this->data);
4546
}
4647

4748
public function testAssertDatabaseHasSupportsModelClass()
4849
{
49-
$this->mockCountBuilder(1);
50+
$this->mockCountBuilder(true);
5051

5152
$this->assertDatabaseHas(ProductStub::class, $this->data);
5253
}
@@ -60,7 +61,7 @@ public function testAssertDatabaseHasConstrainsToModel()
6061
...$this->data,
6162
];
6263

63-
$this->mockCountBuilder(1);
64+
$this->mockCountBuilder(true);
6465

6566
$this->assertDatabaseHas(new ProductStub(['id' => 1]), $data);
6667
}
@@ -70,7 +71,7 @@ public function testSeeInDatabaseDoesNotFindResults()
7071
$this->expectException(ExpectationFailedException::class);
7172
$this->expectExceptionMessage('The table is empty.');
7273

73-
$builder = $this->mockCountBuilder(0);
74+
$builder = $this->mockCountBuilder(false);
7475

7576
$builder->shouldReceive('get')->andReturn(collect());
7677

@@ -83,7 +84,7 @@ public function testSeeInDatabaseFindsNotMatchingResults()
8384

8485
$this->expectExceptionMessage('Found similar results: '.json_encode([['title' => 'Forge']], JSON_PRETTY_PRINT));
8586

86-
$builder = $this->mockCountBuilder(0);
87+
$builder = $this->mockCountBuilder(false);
8788

8889
$builder->shouldReceive('take')->andReturnSelf();
8990
$builder->shouldReceive('get')->andReturn(collect([['title' => 'Forge']]));
@@ -97,8 +98,7 @@ public function testSeeInDatabaseFindsManyNotMatchingResults()
9798

9899
$this->expectExceptionMessage('Found similar results: '.json_encode(['data', 'data', 'data'], JSON_PRETTY_PRINT).' and 2 others.');
99100

100-
$builder = $this->mockCountBuilder(0);
101-
$builder->shouldReceive('count')->andReturn(0, 5);
101+
$builder = $this->mockCountBuilder(false, countResult: [5, 5]);
102102

103103
$builder->shouldReceive('take')->andReturnSelf();
104104
$builder->shouldReceive('get')->andReturn(
@@ -110,14 +110,14 @@ public function testSeeInDatabaseFindsManyNotMatchingResults()
110110

111111
public function testDontSeeInDatabaseDoesNotFindResults()
112112
{
113-
$this->mockCountBuilder(0);
113+
$this->mockCountBuilder(false);
114114

115115
$this->assertDatabaseMissing($this->table, $this->data);
116116
}
117117

118118
public function testAssertDatabaseMissingSupportsModelClass()
119119
{
120-
$this->mockCountBuilder(0);
120+
$this->mockCountBuilder(false);
121121

122122
$this->assertDatabaseMissing(ProductStub::class, $this->data);
123123
}
@@ -131,7 +131,7 @@ public function testAssertDatabaseMissingConstrainsToModel()
131131
...$this->data,
132132
];
133133

134-
$this->mockCountBuilder(0);
134+
$this->mockCountBuilder(false);
135135

136136
$this->assertDatabaseMissing(new ProductStub(['id' => 1]), $data);
137137
}
@@ -140,7 +140,7 @@ public function testDontSeeInDatabaseFindsResults()
140140
{
141141
$this->expectException(ExpectationFailedException::class);
142142

143-
$builder = $this->mockCountBuilder(1);
143+
$builder = $this->mockCountBuilder(true);
144144

145145
$builder->shouldReceive('take')->andReturnSelf();
146146
$builder->shouldReceive('get')->andReturn(collect([$this->data]));
@@ -150,22 +150,22 @@ public function testDontSeeInDatabaseFindsResults()
150150

151151
public function testAssertTableEntriesCount()
152152
{
153-
$this->mockCountBuilder(1);
153+
$this->mockCountBuilder(true);
154154

155155
$this->assertDatabaseCount($this->table, 1);
156156
}
157157

158158
public function testAssertDatabaseCountSupportModels()
159159
{
160-
$this->mockCountBuilder(1);
160+
$this->mockCountBuilder(true);
161161

162162
$this->assertDatabaseCount(ProductStub::class, 1);
163163
$this->assertDatabaseCount(new ProductStub, 1);
164164
}
165165

166166
public function testAssertDatabaseEmpty()
167167
{
168-
$this->mockCountBuilder(0);
168+
$this->mockCountBuilder(false);
169169

170170
$this->assertDatabaseEmpty(ProductStub::class);
171171
$this->assertDatabaseEmpty(new ProductStub);
@@ -175,14 +175,14 @@ public function testAssertTableEntriesCountWrong()
175175
{
176176
$this->expectException(ExpectationFailedException::class);
177177
$this->expectExceptionMessage('Failed asserting that table [products] matches expected entries count of 3. Entries found: 1.');
178-
$this->mockCountBuilder(1);
178+
$this->mockCountBuilder(true);
179179

180180
$this->assertDatabaseCount($this->table, 3);
181181
}
182182

183183
public function testAssertDatabaseMissingPassesWhenDoesNotFindResults()
184184
{
185-
$this->mockCountBuilder(0);
185+
$this->mockCountBuilder(false);
186186

187187
$this->assertDatabaseMissing($this->table, $this->data);
188188
}
@@ -191,7 +191,7 @@ public function testAssertDatabaseMissingFailsWhenFindsResults()
191191
{
192192
$this->expectException(ExpectationFailedException::class);
193193

194-
$builder = $this->mockCountBuilder(1);
194+
$builder = $this->mockCountBuilder(true);
195195

196196
$builder->shouldReceive('get')->andReturn(collect([$this->data]));
197197

@@ -202,7 +202,7 @@ public function testAssertModelMissingPassesWhenDoesNotFindModelResults()
202202
{
203203
$this->data = ['id' => 1];
204204

205-
$builder = $this->mockCountBuilder(0);
205+
$builder = $this->mockCountBuilder(false);
206206

207207
$builder->shouldReceive('get')->andReturn(collect());
208208

@@ -211,14 +211,14 @@ public function testAssertModelMissingPassesWhenDoesNotFindModelResults()
211211

212212
public function testAssertSoftDeletedInDatabaseFindsResults()
213213
{
214-
$this->mockCountBuilder(1);
214+
$this->mockCountBuilder(true);
215215

216216
$this->assertSoftDeleted($this->table, $this->data);
217217
}
218218

219219
public function testAssertSoftDeletedSupportModelStrings()
220220
{
221-
$this->mockCountBuilder(1);
221+
$this->mockCountBuilder(true);
222222

223223
$this->assertSoftDeleted(ProductStub::class, $this->data);
224224
}
@@ -228,7 +228,7 @@ public function testAssertSoftDeletedInDatabaseDoesNotFindResults()
228228
$this->expectException(ExpectationFailedException::class);
229229
$this->expectExceptionMessage('The table is empty.');
230230

231-
$builder = $this->mockCountBuilder(0);
231+
$builder = $this->mockCountBuilder(false);
232232

233233
$builder->shouldReceive('get')->andReturn(collect());
234234

@@ -242,7 +242,7 @@ public function testAssertSoftDeletedInDatabaseDoesNotFindModelResults()
242242

243243
$this->data = ['id' => 1];
244244

245-
$builder = $this->mockCountBuilder(0);
245+
$builder = $this->mockCountBuilder(false);
246246

247247
$builder->shouldReceive('get')->andReturn(collect());
248248

@@ -257,7 +257,7 @@ public function testAssertSoftDeletedInDatabaseDoesNotFindModelWithCustomColumnR
257257
$model = new CustomProductStub(['id' => 1, 'name' => 'Laravel']);
258258
$this->data = ['id' => 1, 'name' => 'Tailwind'];
259259

260-
$builder = $this->mockCountBuilder(0, 'trashed_at');
260+
$builder = $this->mockCountBuilder(false, 'trashed_at');
261261

262262
$builder->shouldReceive('get')->andReturn(collect());
263263

@@ -272,7 +272,7 @@ public function testAssertSoftDeletedInDatabaseDoesNotFindModePassedViaFcnWithCu
272272
$model = new CustomProductStub(['id' => 1, 'name' => 'Laravel']);
273273
$this->data = ['id' => 1];
274274

275-
$builder = $this->mockCountBuilder(0, 'trashed_at');
275+
$builder = $this->mockCountBuilder(false, 'trashed_at');
276276

277277
$builder->shouldReceive('get')->andReturn(collect());
278278

@@ -281,14 +281,14 @@ public function testAssertSoftDeletedInDatabaseDoesNotFindModePassedViaFcnWithCu
281281

282282
public function testAssertNotSoftDeletedInDatabaseFindsResults()
283283
{
284-
$this->mockCountBuilder(1);
284+
$this->mockCountBuilder(true);
285285

286286
$this->assertNotSoftDeleted($this->table, $this->data);
287287
}
288288

289289
public function testAssertNotSoftDeletedSupportModelStrings()
290290
{
291-
$this->mockCountBuilder(1);
291+
$this->mockCountBuilder(true);
292292

293293
$this->assertNotSoftDeleted(ProductStub::class, $this->data);
294294
}
@@ -298,7 +298,7 @@ public function testAssertNotSoftDeletedOnlyFindsMatchingModels()
298298
$this->expectException(ExpectationFailedException::class);
299299
$this->expectExceptionMessage('Failed asserting that any existing row');
300300

301-
$builder = $this->mockCountBuilder(0);
301+
$builder = $this->mockCountBuilder(false);
302302

303303
$builder->shouldReceive('get')->andReturn(collect(), collect(1));
304304

@@ -310,7 +310,7 @@ public function testAssertNotSoftDeletedInDatabaseDoesNotFindResults()
310310
$this->expectException(ExpectationFailedException::class);
311311
$this->expectExceptionMessage('The table is empty.');
312312

313-
$builder = $this->mockCountBuilder(0);
313+
$builder = $this->mockCountBuilder(false);
314314

315315
$builder->shouldReceive('get')->andReturn(collect());
316316

@@ -324,7 +324,7 @@ public function testAssertNotSoftDeletedInDatabaseDoesNotFindModelResults()
324324

325325
$this->data = ['id' => 1];
326326

327-
$builder = $this->mockCountBuilder(0);
327+
$builder = $this->mockCountBuilder(false);
328328

329329
$builder->shouldReceive('get')->andReturn(collect());
330330

@@ -339,7 +339,7 @@ public function testAssertNotSoftDeletedInDatabaseDoesNotFindModelWithCustomColu
339339
$model = new CustomProductStub(['id' => 1, 'name' => 'Laravel']);
340340
$this->data = ['id' => 1, 'name' => 'Tailwind'];
341341

342-
$builder = $this->mockCountBuilder(0, 'trashed_at');
342+
$builder = $this->mockCountBuilder(false, 'trashed_at');
343343

344344
$builder->shouldReceive('get')->andReturn(collect());
345345

@@ -354,7 +354,7 @@ public function testAssertNotSoftDeletedInDatabaseDoesNotFindModelPassedViaFcnWi
354354
$model = new CustomProductStub(['id' => 1, 'name' => 'Laravel']);
355355
$this->data = ['id' => 1];
356356

357-
$builder = $this->mockCountBuilder(0, 'trashed_at');
357+
$builder = $this->mockCountBuilder(false, 'trashed_at');
358358

359359
$builder->shouldReceive('get')->andReturn(collect());
360360

@@ -365,7 +365,7 @@ public function testAssertExistsPassesWhenFindsResults()
365365
{
366366
$this->data = ['id' => 1];
367367

368-
$builder = $this->mockCountBuilder(1);
368+
$builder = $this->mockCountBuilder(true);
369369

370370
$builder->shouldReceive('get')->andReturn(collect($this->data));
371371

@@ -482,10 +482,13 @@ public function testExpectsDatabaseQueryCount()
482482
$case->tearDown();
483483
}
484484

485-
protected function mockCountBuilder($countResult, $deletedAtColumn = 'deleted_at')
485+
protected function mockCountBuilder($existsResult, $deletedAtColumn = 'deleted_at', $countResult = null)
486486
{
487487
$builder = m::mock(Builder::class);
488488

489+
$countResult = Arr::wrap($countResult);
490+
$countResult = ! empty($countResult) ? $countResult : [$existsResult ? 1 : 0];
491+
489492
$key = array_key_first($this->data);
490493
$value = $this->data[$key];
491494

@@ -501,7 +504,9 @@ protected function mockCountBuilder($countResult, $deletedAtColumn = 'deleted_at
501504

502505
$builder->shouldReceive('whereNull')->with($deletedAtColumn)->andReturnSelf();
503506

504-
$builder->shouldReceive('count')->andReturn($countResult)->byDefault();
507+
$builder->shouldReceive('exists')->andReturn($existsResult)->byDefault();
508+
509+
$builder->shouldReceive('count')->andReturn(...$countResult)->byDefault();
505510

506511
$this->connection->shouldReceive('table')
507512
->with($this->table)

0 commit comments

Comments
 (0)