An open API service indexing awesome lists of open source software.

https://github.com/chqthomas/approval-tests-php

A PHP assertion library for approval testing with PHPUnit
https://github.com/chqthomas/approval-tests-php

approval-testing assertion-library phpunit snapshot-testing

Last synced: about 1 month ago
JSON representation

A PHP assertion library for approval testing with PHPUnit

Awesome Lists containing this project

README

        

# PHP Approval Tests

A PHP library for approval testing. This approach allows you to verify complex results by comparing them with approved versions, making it ideal for testing outputs that are difficult to assert traditionally (e.g., HTML, JSON, XML, or binary files).

> [!WARNING]
> This library is still in development. It is not recommended for production use. Many features are still missing, and the API may change.

## Table of Contents

- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Simple Test](#simple-test)
- [Structured Data Test](#structured-data-test)
- [Specialized Verifications](#specialized-verifications)
- [HTML](#html)
- [JSON](#json)
- [XML](#xml)
- [CSV](#csv)
- [Binary Files](#binary-files)
- [Advanced Features](#advanced-features)
- [Tests with Data Providers](#tests-with-data-providers)
- [Verify All Combinations](#verify-all-combinations)
- [Configuration](#configuration)
- [PHPUnit Bootstrap Configuration](#phpunit-bootstrap-configuration)
- [Set a Custom Reporter](#set-a-custom-reporter)
- [Use a Custom Object Formatter](#use-a-custom-object-formatter)
- [Custom Namer](#custom-namer)
- [Auto-Approve Snapshots](#auto-approve-snapshots)
- [Scrubbers](#scrubbers)
- [JSON Scrubbing](#json-scrubbing)
- [Ignore JSON Members](#ignore-json-members)
- [Scrub JSON Members](#scrub-json-members)
- [XML Scrubbing](#xml-scrubbing)
- [Regex Scrubbing](#regex-scrubbing)
- [Custom Scrubber](#custom-scrubber)
- [Maintenance](#maintenance)
- [Cleanup Received Files](#cleanup-received-files)
- [Detect Orphaned Files](#detect-orphaned-files)
- [Reporters](#reporters)
- [CLI Reporter](#cli-reporter)
- [Diff Reporter](#diff-reporter)
- [Composite Reporter](#composite-reporter)
- [Symfony Integration](#symfony-integration)
- [Best Practices](#best-practices)
- [Contributing](#contributing)
- [License](#license)

## Installation

Install the library via Composer:

```php
composer require chqthomas/approval-tests
```

## Basic Usage

### Simple Test

Verify a simple string output:

```php
use ChqThomas\ApprovalTests\Approvals;

public function testSimpleString(): void
{
Approvals::verify("Hello World");
}
```

The first time this runs, it generates a `.received.txt` file. Approve it by renaming it to `.approved.txt` or use auto-approval (see below).

### Structured Data Test

Verify complex data structures like arrays or objects:

```php
public function testArray(): void
{
$data = [
'name' => 'John Doe',
'age' => 30,
'roles' => ['admin', 'user']
];
Approvals::verify($data);
}
```

## Specialized Verifications

The library supports specific formats with dedicated methods:

### HTML

Verify HTML content with automatic formatting:

```php
public function testHtml(): void
{
$html = '

Hello World
';
Approvals::verifyHtml($html);
}
```

### JSON

Verify JSON with pretty-printing and scrubbing:

```php
public function testJson(): void
{
$json = '{"name":"John","age":30}';
Approvals::verifyJson($json); // Automatically formatted
}
```

### XML

Verify XML with formatting:

```php
public function testXml(): void
{
$xml = 'John';
Approvals::verifyXml($xml);
}
```

### CSV

Verify CSV content:

```php
public function testCsv(): void
{
$csv = "name,age\nJohn,30\nJane,25";
Approvals::verifyCsv($csv);
}
```

### Binary Files

Verify binary content (e.g., images):

```php
public function testBinaryFile(): void
{
Approvals::verifyBinaryFile('path/to/image.png', 'png');
}
```

## Advanced Features

### Tests with Data Providers

Use PHPUnit data providers for parameterized tests:

```php
/**
* @dataProvider provideTestData
*/
public function testWithDataProvider(array $data, string $expected): void
{
$result = processData($data);
Approvals::verify($result);
}

public static function provideTestData(): array
{
return [
'case1' => [['input' => 1], 'output1'],
'case2' => [['input' => 2], 'output2'],
];
}
```

### Verify All Combinations

Test all combinations of inputs:

```php
public function testAllCombinations(): void
{
$operations = ['+', '-', '*', '/'];
$numbers = [1, 2, 3];

Approvals::verifyAllCombinations(
function($op, $a, $b) {
switch($op) {
case '+': return $a + $b;
case '-': return $a - $b;
case '*': return $a * $b;
case '/': return $b != 0 ? $a / $b : 'Division by zero';
}
},
[$operations, $numbers, $numbers]
);
}
```

## Configuration

Customize the library’s behavior via the `Configuration` class:

### PHPUnit Bootstrap Configuration

Create a `tests/bootstrap.php` file to configure the library globally for all your tests:

```php
setReporter(new DiffReporter())
->setObjectFormatter(new SymfonyObjectFormatter())
->setAutoApprove(false);

// Configure default scrubbers for specific formats
Configuration::getInstance()
->setDefaultScrubber('json', JsonScrubber::create()
->scrubMember('password', 'token')
->ignoreMember('sensitive_data'))
->setDefaultScrubber('xml', XmlScrubber::create()
->addScrubber(RegexScrubber::create([
'/\d{4}-\d{2}-\d{2}/' => '[DATE]'
])));
```

Then reference it in your phpunit.xml:

```xml

```

### Set a Custom Reporter

Change how differences are reported:

```php
use ChqThomas\ApprovalTests\Configuration;
use ChqThomas\ApprovalTests\Reporter\DiffReporter;

Configuration::getInstance()->setReporter(new DiffReporter());
```

### Use a Custom Object Formatter

Switch between default and Symfony formatters:

```php
use ChqThomas\ApprovalTests\Formatter\SymfonyObjectFormatter;

Configuration::getInstance()->setObjectFormatter(new SymfonyObjectFormatter());
```

*Note*: Requires `symfony/serializer` to be installed for `SymfonyObjectFormatter`.

### Custom Namer

Set a custom namer for file naming:

```php
use ChqThomas\ApprovalTests\Namer\EnvironmentAwareNamer;

Configuration::getInstance()->setNamerClass(EnvironmentAwareNamer::class);
```

### Auto-Approve Snapshots

Automatically approve new or changed snapshots:

```php
Configuration::getInstance()->setAutoApprove(true);
```

Or use an environment variable:

```php
APPROVE_SNAPSHOTS=true vendor/bin/phpunit
```

## Scrubbers

Scrubbers normalize content before comparison, handling dynamic data like dates or IDs.

### JSON Scrubbing

Scrub sensitive or variable data:

```php
public function testJsonScrubbing(): void
{
$json = <<ignoreMember('sensitive')); // Member will be removed
}
```

#### Scrub JSON Members

Replace members with a placeholder:

```php
public function testJsonScrubMember(): void
{
$json = <<scrubMember('password', 'api_key')); // Members will be replaced with "[scrubbed]"
}
```

### XML Scrubbing

Custom scrubbing for XML:

```php
public function testXmlScrubbing(): void
{
$xml = <<

John
2024-01-01T12:00:00
550e8400-e29b-41d4-a716-446655440000

XML;

// Custom scrubber for XML
Approvals::verifyXml($xml, XmlScrubber::create()
->addScrubber(fn($content) => preg_replace('/John/', '[NAME]', $content)));
}
```

### Regex Scrubbing

Use regular expressions for generic scrubbing:

```php
public function testRegexScrubbing(): void
{
$json = <<addScrubber(RegexScrubber::create(['/"id": "([A-Z]{3}\d{3})"/' => '"id": "MATCHED"'])));
}
```

### Custom Scrubber

Create a custom scrubber for any content:

```php
use ChqThomas\ApprovalTests\Scrubber\AbstractScrubber;

class MyScrubber extends AbstractScrubber
{
public function scrub(string $content): string
{
// Apply base scrubbers first (GUIDs, dates)
$content = $this->scrubGuids($content);
$content = $this->scrubDates($content);

// Add your custom rules
$content = preg_replace('/secret-\d+/', '[SECRET]', $content);

// Apply additional scrubbers
return $this->applyAdditionalScrubbers($content);
}
}

// Usage
public function testWithCustomScrubber(): void
{
$content = "ID: secret-123\nDate: 2024-01-01";

Approvals::verifyWithExtension(
$content,
"txt",
MyScrubber::create()
->addScrubber(fn($text) => str_replace('ID:', 'Reference:', $text))
);
}
```

## Maintenance

### Cleanup Received Files

Remove redundant `.received` files:

```php
use ChqThomas\ApprovalTests\ApprovalMaintenance;

ApprovalMaintenance::cleanUpReceivedFiles(__DIR__ . '/tests/approvals');
```

### Detect Orphaned Files

Find `.approved` files without associated tests:

```php
$orphanedFiles = ApprovalMaintenance::findOrphanedApprovedFiles(__DIR__ . '/tests');
```

## Reporters

Customize how differences are reported:

### CLI Reporter

Default reporter for terminal output:

```php
use ChqThomas\ApprovalTests\Reporter\CliReporter;

Configuration::getInstance()->setReporter(new CliReporter());
```

### Diff Reporter

Show differences using a diff format:

```php
use ChqThomas\ApprovalTests\Reporter\DiffReporter;

Configuration::getInstance()->setReporter(new DiffReporter());
```

### Composite Reporter

Combine multiple reporters:

```php
use ChqThomas\ApprovalTests\Reporter\CompositeReporter;

$reporter = new CompositeReporter([
new CliReporter(),
new DiffReporter()
]);
Configuration::getInstance()->setReporter($reporter);
```

## Symfony Integration

Use with Symfony’s DomCrawler for web testing:

```php
use ChqThomas\ApprovalTests\Symfony\ApprovalCrawlerAssertionsTrait;

class MyWebTest extends WebTestCase
{
use ApprovalCrawlerAssertionsTrait;

public function testPageContent(): void
{
$client = static::createClient();
$client->request('GET', '/');
self::verifySelectorHtml('#main-content');
}
}
```

## Best Practices

1. Store `.approved` files in version control.
2. Use scrubbers for variable data (e.g., dates, IDs).
3. Regularly clean up `.received` files.
4. Check for orphaned `.approved` files.
5. Use descriptive test names for clear file naming.

## Contributing

Contributions are welcome! To contribute:
1. Fork the project.
2. Create a feature branch.
3. Submit a pull request.

## License

MIT License