PHPackages                             markhuot/craft-pest - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. markhuot/craft-pest

ActiveCraft-plugin[Testing &amp; Quality](/categories/testing)

markhuot/craft-pest
===================

A Pest runner

2.0.0(1y ago)439.1k113proprietaryPHP

Since Apr 6Pushed 1y ago5 watchersCompare

[ Source](https://github.com/markhuot/craft-pest)[ Packagist](https://packagist.org/packages/markhuot/craft-pest)[ RSS](/packages/markhuot-craft-pest/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (2)Versions (20)Used By (3)

[![craft-pest screen shot](./screenshot.png)](./screenshot.png)

Pest for Craft CMS
==================

[](#pest-for-craft-cms)

```
composer require markhuot/craft-pest
./craft plugin/install pest
./craft pest/test
```

Handles the setup and installation of [Pest](https://pestphp.com) in to [Craft CMS](https://craftcms.com). This allows you to write tests that look something like this!

```
it('loads the homepage')
    ->get('/')
    ->assertOk();

it('has a welcoming h1 element')
    ->get('/')
    ->expectSelector('h1')
    ->text->toBe('Welcome');

it('asserts nine list items')
    ->get('/')
    ->querySelector('li')
    ->assertCount(9);

it('promotes craft')
    ->get('/')
    ->assertHeader('x-powered-by', 'Craft CMS');

it('shows news on the homepage', function() {
    $titles = News::factory()->count(3)->create()->title;

    $this->get('/')
        ->expectSelector('.news__title')
        ->text->sequence(...$titles);
});
```

Craft Pest provides a number of testing aids to improve the developer experience while writing tests. The goal is to make test writing as easy as possible so more tests are written over the course of the project. Additional documentation on each feature can be found in the `/docs` folder. The following "kitchen sink" test has examples of most of them.

```
it('tests the kitchen sink', function () {
    // Factories can be used to create fields, sections, entries, etc… Realistically, much
    // of this may come from your project.yaml, but in the event you need to scaffold some
    // content types while testing a plugin or module, it is absolutely possible.
    // What's more, if a project.yaml is detected, Craft Pest will automatically check and
    // apply that config before each run to ensure you are always testing against a clean
    // schema.
    $section = Section::factory()->create();

    // Volume factories give you a local folder-based volume that ensures
    // your tests don't clutter your production S3 buckets, for example.
    $volume = Volume::factory()->create();

    $entry = Entry::factory()

        // Most fields on the entry factory can be defined just like you would'
        // when querying `craft.entries` in a template.
        ->section($section->handle)

        // Even custom fields can be defined while creating an entry. This allows
        // you to mock/simulate a variety of content elements without needing to
        // pass around a complex and huge "seeding" database.
        ->isPromoted(true)

        // Custom fields can be set to nested factories and will be automatically
        // created as they are needed.
        // Most factories can be utilized with as few as one additional field, like
        // assets here, which only need to know their volume. The contents of the
        // image will default to a 500x500px gray square.
        ->heroImage(Asset::factory()->volume($volume->handle))
        ->create();

    // For simple tests, you can call `->get()` or `->post()` to make simulated
    // HTTP requests against the Craft site. It will take a site (or CP) URL and
    // return the rendered response.
    // For many people, just starting out with testing, their first (and only)
    // test is simply `$this->get('/')` to ensure the homepage loads.
    $this->get($entry->uri)

        // All HTTP tests should probably check the status code of the response and
        // ensure Craft returned a 200 Ok response.
        ->assertOk()

        // A response can be further inspected to check that the exact HTML matches
        // your expectations. Here the `querySelector` and `expectSelector` methods
        // allow you to parse over the response using a familiar CSS-based syntax.
        ->expectSelector('h1')

        // Querying the HTML allows you to test the text, count, or even HTML of the
        // DOM to ensure it matches your expectations.
        ->text->toBe('Welcome');
});
```

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance36

Infrequent updates — may be unmaintained

Popularity36

Limited adoption so far

Community25

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 94.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~70 days

Recently: every ~174 days

Total

14

Last Release

595d ago

Major Versions

1.0.0 → 2.0.02024-10-01

### Community

Maintainers

![](https://www.gravatar.com/avatar/2628c4f6f6afddfe0b861cee15ae5b18c673d874f68786ebd1d01bba2dd6bdfb?d=identicon)[markhuot](/maintainers/markhuot)

---

Top Contributors

[![markhuot](https://avatars.githubusercontent.com/u/48975?v=4)](https://github.com/markhuot "markhuot (575 commits)")[![aaronbushnell](https://avatars.githubusercontent.com/u/315202?v=4)](https://github.com/aaronbushnell "aaronbushnell (11 commits)")[![myleshyson](https://avatars.githubusercontent.com/u/11747187?v=4)](https://github.com/myleshyson "myleshyson (7 commits)")[![bencroker](https://avatars.githubusercontent.com/u/57572400?v=4)](https://github.com/bencroker "bencroker (5 commits)")[![nfourtythree](https://avatars.githubusercontent.com/u/266453?v=4)](https://github.com/nfourtythree "nfourtythree (4 commits)")[![Alxmerino](https://avatars.githubusercontent.com/u/1016021?v=4)](https://github.com/Alxmerino "Alxmerino (3 commits)")[![Diewy](https://avatars.githubusercontent.com/u/1014380?v=4)](https://github.com/Diewy "Diewy (1 commits)")

### Embed Badge

![Health badge](/badges/markhuot-craft-pest/health.svg)

```
[![Health](https://phpackages.com/badges/markhuot-craft-pest/health.svg)](https://phpackages.com/packages/markhuot-craft-pest)
```

###  Alternatives

[phpspec/prophecy

Highly opinionated mocking framework for PHP 5.3+

8.5k551.7M682](/packages/phpspec-prophecy)[brianium/paratest

Parallel testing for PHP

2.5k118.8M754](/packages/brianium-paratest)[instaclick/php-webdriver

PHP WebDriver for Selenium 2

43661.8M22](/packages/instaclick-php-webdriver)[spatie/phpunit-snapshot-assertions

Snapshot testing with PHPUnit

69617.9M510](/packages/spatie-phpunit-snapshot-assertions)[szepeviktor/phpstan-wordpress

WordPress extensions for PHPStan

3287.8M898](/packages/szepeviktor-phpstan-wordpress)[dms/phpunit-arraysubset-asserts

This package provides ArraySubset and related asserts once deprecated in PHPUnit 8

14327.7M294](/packages/dms-phpunit-arraysubset-asserts)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
