Pest Architecture, Mutation &amp; Type Coverage in Laravel | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI

 Laravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI
============================================================================

 Go beyond feature tests. Learn how to enforce architectural rules, catch logic gaps with mutation testing, and track type coverage using Pest — all wired into a practical CI pipeline.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 16 Jun 2026 · Updated 16 Jun 2026 · 4 min read

ShareCopy linkCopied

 ![Laravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI](https://cdn.msaied.com/214/0d4822fa8ee1765d0689e387dd849d92.png) 

  On this page +1. [Beyond Feature Tests: Pest as a Quality Gate](#beyond-feature-tests-pest-as-a-quality-gate)
2. [Architecture Tests](#architecture-tests)
3. [Preset Shortcuts](#preset-shortcuts)
4. [Mutation Testing with Infection](#mutation-testing-with-infection)
5. [Type Coverage](#type-coverage)
6. [Wiring It All Into CI](#wiring-it-all-into-ci)
7. [Takeaways](#takeaways)

 Beyond Feature Tests: Pest as a Quality Gate
--------------------------------------------

Most Laravel teams use Pest for feature and unit tests and stop there. That leaves two powerful capabilities on the table: **architecture tests** that enforce structural rules, and **mutation testing** that proves your assertions actually catch bugs. Add type coverage reporting and you have a three-layer quality gate that belongs in every serious CI pipeline.

---

Architecture Tests
------------------

Pest's `arch()` helper lets you encode conventions as executable rules. No more PR comments about "don't use facades in domain code."

```php
// tests/Architecture/DomainTest.php

arch('domain classes are pure PHP')
    ->expect('App\Domain')
    ->not->toUse(['Illuminate\Support\Facades\DB', 'Illuminate\Support\Facades\Cache'])
    ->not->toExtend('Illuminate\Database\Eloquent\Model');

arch('actions are invokable and final')
    ->expect('App\Actions')
    ->toBeFinal()
    ->toHaveMethod('__invoke');

arch('no debug calls survive')
    ->expect('App')
    ->not->toUse(['dd', 'dump', 'ray', 'var_dump']);

```

These run in milliseconds and fail the build the moment someone sneaks an Eloquent model into a domain value object. The `toUse` check is a static analysis pass over the AST — no runtime overhead.

### Preset Shortcuts

Pest ships presets for common Laravel conventions:

```php
arch()->preset()->laravel();   // controllers, models, jobs follow framework conventions
arch()->preset()->strict();    // strict types, no unused imports, typed properties
arch()->preset()->security();  // no eval, no exec, no shell_exec

```

Layer your own rules on top of presets rather than replacing them.

---

Mutation Testing with Infection
-------------------------------

Architecture tests guard structure; mutation testing guards logic. [Infection](https://infection.github.io) modifies your source code in small ways (mutants) and checks whether your test suite catches each change.

Install it as a dev dependency:

```bash
composer require --dev infection/infection infection/codecoverage

```

A minimal `infection.json5` for a Laravel project:

```json5
{
  "source": { "directories": ["app/Domain", "app/Actions"] },
  "testFramework": "pest",
  "minMsi": 80,
  "minCoveredMsi": 90,
  "mutators": { "@default": true }
}

```

`minMsi` (Mutation Score Indicator) is the percentage of mutants killed. Setting it to 80 means 20 % of logic changes can slip past your tests — tune it upward as coverage matures.

Run it locally:

```bash
vendor/bin/infection --threads=4 --show-mutations

```

A surviving mutant on a pricing calculation is a concrete signal that your test only checks the happy path, not boundary conditions.

---

Type Coverage
-------------

Pest 2.x ships a `--type-coverage` flag backed by a static analysis pass. It reports the percentage of return types, parameter types, and property types declared across your codebase.

```bash
vendor/bin/pest --type-coverage --min=90

```

Fail the build if coverage drops below your threshold:

```php
// pest.php
covers(\App\Domain\Pricing\PriceCalculator::class);

```

This is not a replacement for PHPStan or Psalm — it is a fast, CI-friendly gate that catches regressions when someone adds an untyped helper method.

---

Wiring It All Into CI
---------------------

A GitHub Actions job that runs all three layers in parallel:

```yaml
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3', coverage: pcov }
      - run: composer install --no-interaction
      - run: vendor/bin/pest --parallel --coverage --min=80
      - run: vendor/bin/pest --type-coverage --min=90
      - run: vendor/bin/infection --threads=4 --min-msi=80

```

Keep architecture tests in a separate `--group=arch` so they run first and fail fast without waiting for the full suite.

```php
describe('arch', function () {
    // ...
})->group('arch');

```

```bash
vendor/bin/pest --group=arch  # fast gate, ~2 s
vendor/bin/pest --parallel    # full suite

```

---

Takeaways
---------

- **Architecture tests** are zero-runtime structural guards — encode your team's conventions before they become tribal knowledge.
- **Mutation testing** reveals tests that pass for the wrong reasons; target domain logic and pricing/calculation code first.
- **Type coverage** is a lightweight proxy for codebase health; pair it with PHPStan level 8 for full static analysis.
- Run architecture tests as a fast first gate; mutation testing is slow and belongs after the green suite.
- `minMsi` thresholds should increase over time — treat them like code coverage floors, not ceilings.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [pest](https://www.msaied.com/public/articles?search=pest)
- [testing](https://www.msaied.com/public/articles?search=testing)
- [ci](https://www.msaied.com/public/articles?search=ci)
- [php](https://www.msaied.com/public/articles?search=php)

 Frequently asked questions 
---------------------------

  Does mutation testing replace code coverage metrics?No — they measure different things. Code coverage tells you which lines were executed; mutation testing tells you whether your assertions would catch a logic change. High coverage with low MSI means your tests run the code but don't verify its behaviour. Use both.

   Are Pest architecture tests the same as PHPStan rules?They overlap but serve different purposes. Pest arch tests are written in PHP alongside your test suite and enforce project-specific conventions (e.g. no facades in domain code). PHPStan rules are more granular type-level checks. Run both: arch tests for team conventions, PHPStan for type correctness.

   How slow is Infection on a large Laravel codebase?Infection can be slow because it re-runs the test suite for every mutant. Scope it to high-value directories (domain logic, actions) rather than the whole app, use `--threads` to parallelise, and run it on a nightly CI schedule rather than every push.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel Pennant: Feature Flags with Scope, Storage, and Custom Drivers](https://www.msaied.com/public/articles/laravel-pennant-feature-flags-with-scope-storage-and-custom-drivers) [Next articleRAG Pipelines in Laravel: Chunking, Embedding, and Retrieval with pgvector](https://www.msaied.com/public/articles/rag-pipelines-in-laravel-chunking-embedding-and-retrieval-with-pgvector)  

   On this page
-------------

1. [Beyond Feature Tests: Pest as a Quality Gate](#beyond-feature-tests-pest-as-a-quality-gate)
2. [Architecture Tests](#architecture-tests)
3. [Preset Shortcuts](#preset-shortcuts)
4. [Mutation Testing with Infection](#mutation-testing-with-infection)
5. [Type Coverage](#type-coverage)
6. [Wiring It All Into CI](#wiring-it-all-into-ci)
7. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
