Skip to content
Back to skills

Test

ASecurity

Testing patterns for PHPUnit tests. Use when writing tests, debugging test failures, setting up test coverage, or implementing test patterns for ATmosphere features.

  • 53 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 9, 2026
documentationphpbashsqltestingdebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add Automattic/wordpress-atmosphere --skill test --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/automattic-test/badge)](https://www.skillsdirectory.com/skills/automattic-test)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test
description: Testing patterns for PHPUnit tests. Use when writing tests, debugging test failures, setting up test coverage, or implementing test patterns for ATmosphere features.
---

# ATmosphere Testing

## Quick Reference

### Key Commands
- **All tests:** `npm run env-test`
- **Single test:** `npm run env-test -- --filter=test_name`
- **Local (needs MySQL):** `composer test`

## PHPUnit Testing

### Test Structure

```php
<?php
namespace Atmosphere\Tests;

use WP_UnitTestCase;

class Test_Feature extends WP_UnitTestCase {
    public function set_up(): void {
        parent::set_up();
        // Setup.
    }

    public function tear_down(): void {
        // Cleanup.
        parent::tear_down();
    }

    public function test_functionality() {
        // Test implementation.
    }
}
```

### Test File Location

Tests live in `tests/phpunit/tests/`, mirroring `includes/` (run `ls -R tests/phpunit/tests` for the current listing):

```
tests/phpunit/tests/
├── class-test-*.php        # Core classes. class-test-publisher.php is the largest fixture;
│                           # it exposes register_capture() / $captured_calls / $fail_call_indexes.
├── cli/                    # WP-CLI command tests.
├── content-parser/         # Parser tests + Fake_Parser / Minimal_Parser stubs + trait-block-fixtures.php.
├── oauth/                  # Client authorize/refresh, encryption, resolver, redirect-uri filter.
├── rest/                   # REST controllers (admin/ for the pre-publish controller).
├── transformer/            # One test file per transformer + class-stub-parser.php.
└── wp-admin/               # Settings, notices, sanitize callbacks.
```

Test files are prefixed with `class-test-` and the class name is `Test_` + feature name.

### Test Groups

Use `@group` annotations:
```php
/**
 * @group atmosphere
 * @group transformer
 */
public function test_transformer_feature() {
    // Test code.
}
```

### HTTP Requests in Tests

All HTTP requests are disabled by default in the bootstrap. To allow specific requests:
```php
add_filter( 'tests_allow_http_request', function( $allow, $args, $url ) {
    if ( str_contains( $url, 'expected-domain.com' ) ) {
        return true;
    }
    return $allow;
}, 10, 3 );
```

## Debugging Tests

```bash
# Run with verbose output.
npm run env-test -- --verbose --debug

# Stop on first failure.
npm run env-test -- --stop-on-failure

# Run single test method.
npm run env-test -- --filter=test_specific_method
```

## Common Fixtures

```php
// Connected-account fixture — get_did() etc. read the identity option.
\update_option( 'atmosphere_identity', array( 'did' => 'did:plc:test123' ), false );
// Delete it in tear_down().

// Stub blob uploads (thumbnails, icons) without HTTP — records the attempt too.
$attempted     = false;
$short_circuit = static function () use ( &$attempted ) {
	$attempted = true;
	return array( 'blob' => array( 'cid' => 'bafytest' ) );
};
\add_filter( 'atmosphere_pre_upload_blob', $short_circuit );

// HTTP tripwire — fail loudly if any code path fires a real request.
$fetched  = false;
$tripwire = static function ( $preempt ) use ( &$fetched ) {
	$fetched = true;
	return $preempt;
};
\add_filter( 'pre_http_request', $tripwire, 1, 1 );
// ...exercise...
$this->assertFalse( $fetched, 'No HTTP request should be made.' );
```

Asserting a path is read-only? Pair the tripwire with a meta assertion (e.g. `Document::META_TID` still empty) — GET-serving preview paths must not write publish-state meta.

## Stubbing `applyWrites` calls

The Publisher test fixture (`Test_Publisher`) exposes `register_capture()` plus `$captured_calls` / `$fail_call_indexes` for asserting on the `writes` batch and forcing per-call failures:

```php
$this->fail_call_indexes = array(
    2 => new \WP_Error( 'atmosphere_pds_500', 'PDS rejected.' ),
);
$this->register_capture( $post_id );
// ...exercise...
$this->assertCount( 3, $this->captured_calls );
```

Outside the Publisher test, hook the `atmosphere_pre_apply_writes` filter directly (see Publisher::apply_writes — short-circuits before the HTTP layer, so DPoP-less test environments work).

## Simulating in-flight races

To reproduce a "state changed during the API call" race in tests, mutate the WP state from inside the `atmosphere_pre_apply_writes` filter callback and return a synthetic 2xx response. The plugin's hooks fire synchronously in the test process — the filter callback is the analogue of "the API call took long enough for another request to land".

```php
\add_filter(
    'atmosphere_pre_apply_writes',
    static function ( $short, $writes ) use ( $comment_id ) {
        \wp_set_comment_status( $comment_id, 'hold' );
        return array( 'results' => array( /* synthetic */ ) );
    },
    10,
    2
);
```

Note: `wp_delete_comment( $id, true )` removes commentmeta synchronously, which can erase TIDs the reconcile path needs. Prefer status transitions (`hold`, `spam`) when possible.

## Cron handlers in tests

The plugin's `register_async_hooks()` runs at `plugins_loaded` (via the bootstrap), so cron handlers ARE registered before tests execute. Use `\do_action( 'atmosphere_publish_comment', $comment_id )` to fire a handler synchronously; assert on `\wp_next_scheduled()` for follow-up scheduling.

Always clean up scheduled hooks in `tear_down()` — leftover events from one test become flaky preconditions for the next.

## See Also

- [`docs/development-environment.md`](../../../docs/development-environment.md) — PHPUnit argument reference, coverage reports, troubleshooting wp-env.
- [`docs/php-coding-standards.md → Cron-Specific Rules`](../../../docs/php-coding-standards.md#cron-specific-rules) — the publish/update/delete patterns the tests are exercising.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.