Skip to content

Commit c88c244

Browse files
committed
feat: add environment variable support for test snapshot modes
Add UPDATE_EMBED_SNAPSHOTS environment variable to update both HTTP response cache and fixtures together when running tests. Changes: - Add UPDATE_EMBED_SNAPSHOTS=1 to fetch from network and update snapshots - Add EMBED_STRICT_CACHE=1 to fail if cache is missing (for CI) - Update GitHub Actions workflow to use EMBED_STRICT_CACHE - Add comprehensive documentation for snapshot testing in README Usage: UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit --filter testYoutube
1 parent a0bdfef commit c88c244

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242

4343
- name: Tests
4444
run: composer test
45+
env:
46+
EMBED_STRICT_CACHE: 1
4547

4648
phpstan:
4749
name: PHPStan Static Analysis

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,74 @@ Note: The built-in detectors does not require settings. This feature is only for
352352

353353
---
354354

355+
## Testing
356+
357+
### Running tests
358+
359+
```bash
360+
composer test
361+
# or
362+
./vendor/bin/phpunit
363+
```
364+
365+
### Snapshot modes
366+
367+
The test suite uses cached HTTP responses and fixtures to avoid network requests during testing. You can control this behavior using environment variables:
368+
369+
| Environment Variable | Description |
370+
|---------------------|-------------|
371+
| `UPDATE_EMBED_SNAPSHOTS=1` | Fetch from network and update both cache and fixtures |
372+
| `EMBED_STRICT_CACHE=1` | Fail if cache or fixture doesn't exist (useful for CI) |
373+
374+
By default (no environment variables set), tests read from cache and generate missing files automatically.
375+
376+
**Note:** If both `UPDATE_EMBED_SNAPSHOTS` and `EMBED_STRICT_CACHE` are set, `UPDATE_EMBED_SNAPSHOTS` takes precedence.
377+
378+
### Cache structure
379+
380+
The test framework uses two types of cached data:
381+
382+
- **Response cache** (`tests/cache/`): Cached HTTP responses from external sites
383+
- **Fixtures** (`tests/fixtures/`): Expected test results (metadata extracted from cached responses)
384+
385+
### How to update cache
386+
387+
#### When a website changes its HTML structure
388+
389+
If a website updates its HTML and you need to update the cached response and fixture:
390+
391+
```bash
392+
# Update cache and fixture for a specific test
393+
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit --filter testYoutube
394+
```
395+
396+
#### When adding a new test
397+
398+
After adding a new URL to test:
399+
400+
```bash
401+
# This will fetch the response and create both cache and fixture
402+
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit --filter testNewSite
403+
```
404+
405+
#### Update all caches at once
406+
407+
To refresh all cached responses and fixtures from the network:
408+
409+
```bash
410+
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit
411+
```
412+
413+
#### For CI environments
414+
415+
Ensure all tests run strictly from cache (fail if any cache is missing):
416+
417+
```bash
418+
EMBED_STRICT_CACHE=1 ./vendor/bin/phpunit
419+
```
420+
421+
---
422+
355423
[ico-version]: https://poser.pugx.org/embed/embed/v/stable
356424
[ico-license]: https://poser.pugx.org/embed/embed/license
357425
[ico-downloads]: https://poser.pugx.org/embed/embed/downloads

tests/PagesTestCase.php

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,27 @@
1515

1616
abstract class PagesTestCase extends TestCase
1717
{
18+
/**
19+
* Cache mode
20+
* -1 = Read from cache (throws an exception if the cache doesn't exist)
21+
* 0 = Read from cache (generate the files the first time)
22+
* 1 = Read from net without overriding the cache files
23+
* 2 = Read from net and override the cache files
24+
*
25+
* Can be overridden by environment variables:
26+
* - UPDATE_EMBED_SNAPSHOTS=1 : Fetch from network and update both cache and fixtures
27+
* - EMBED_STRICT_CACHE=1 : Fail if cache or fixture doesn't exist (mode -1)
28+
*/
1829
const CACHE = 0;
30+
31+
/**
32+
* Fixtures mode
33+
* 0 = Do not override the fixtures
34+
* 1 = Override the fixtures
35+
*
36+
* Can be overridden by environment variable:
37+
* - UPDATE_EMBED_SNAPSHOTS=1 : Update both cache and fixtures
38+
*/
1939
const FIXTURES = 0;
2040

2141
private const DETECTORS = [
@@ -42,14 +62,53 @@ abstract class PagesTestCase extends TestCase
4262

4363
private static Embed $embed;
4464

65+
/**
66+
* Get cache mode from environment variables or class constant
67+
*/
68+
protected static function getCacheMode(): int
69+
{
70+
// UPDATE_EMBED_SNAPSHOTS=1 : Fetch from network and override cache
71+
if (getenv('UPDATE_EMBED_SNAPSHOTS')) {
72+
return 2;
73+
}
74+
75+
// EMBED_STRICT_CACHE=1 : Fail if cache doesn't exist
76+
if (getenv('EMBED_STRICT_CACHE')) {
77+
return -1;
78+
}
79+
80+
return static::CACHE;
81+
}
82+
83+
/**
84+
* Get fixtures mode from environment variable or class constant
85+
*/
86+
protected static function getFixturesMode(): int
87+
{
88+
// UPDATE_EMBED_SNAPSHOTS=1 : Override fixtures with new results
89+
if (getenv('UPDATE_EMBED_SNAPSHOTS')) {
90+
return 1;
91+
}
92+
93+
return static::FIXTURES;
94+
}
95+
96+
/**
97+
* Check if strict cache mode is enabled
98+
*/
99+
protected static function isStrictMode(): bool
100+
{
101+
return (bool) getenv('EMBED_STRICT_CACHE');
102+
}
103+
45104
private static function getEmbed(): Embed
46105
{
47106
if (isset(self::$embed)) {
48107
return self::$embed;
49108
}
50109

51110
$dispatcher = new FileClient(__DIR__.'/cache');
52-
$dispatcher->setMode(static::CACHE);
111+
$dispatcher->setMode(self::getCacheMode());
53112

54113
return self::$embed = new Embed(new Crawler($dispatcher), self::getExtractorFactory());
55114
}
@@ -63,7 +122,18 @@ protected function assertEmbed(string $url)
63122
$data = self::getData($extractor);
64123
$expected = self::readData($uri);
65124

66-
if (!$expected || static::FIXTURES === 1) {
125+
// In strict mode, fail if fixture is missing
126+
if (!$expected && self::isStrictMode()) {
127+
$this->fail("Fixture not found for {$url}");
128+
}
129+
130+
// Update mode: save fixture and skip
131+
if (self::getFixturesMode() === 1) {
132+
self::writeData($uri, $data);
133+
echo PHP_EOL."Save fixture: {$url}";
134+
$this->markTestSkipped('Skipped assertion for '.$url);
135+
} elseif (!$expected) {
136+
// Missing fixture in non-strict mode: create and skip
67137
self::writeData($uri, $data);
68138
echo PHP_EOL."Save fixture: {$url}";
69139
$this->markTestSkipped('Skipped assertion for '.$url);

0 commit comments

Comments
 (0)