Testing Craft CMS

Max Myers, Senior Platform Developer

Article Categories: #Code, #Front-end Engineering, #Back-end Engineering, #Tooling

Posted on

If you’re building a Craft CMS project and not testing, this post is your practical, no-nonsense starting point.

Testing doesn’t have to be formal, complicated, or overwhelming. At its core, it’s just about making sure the thing you built works - and continues to work as things change. That could mean checking if a string is turned into a slug correctly, making sure a contact form submits, or confirming that your homepage layout didn’t fall apart after a plugin update.


Why Testing Matters #

Testing helps us:

  • Catch bugs before users do – Avoid regressions that slip in from code or plugin changes.
  • Document what’s supposed to happen – Tests describe expected behavior and make it easier to revisit code later.
  • Ship faster – Cut down on manual QA time and gain confidence in your PRs.
  • Refactor safely – Clean up code without fear of unintended side effects.
  • Feel more confident – Testing reduces guesswork and anxiety when making changes.

Bottom line: testing isn’t about writing more code — it’s about protecting the code you already have and making future changes less risky.


What Testing Really Means #

Testing isn’t just one thing – there are different types, each checking a different level of your app:

🔹 Unit Tests #

Small, focused tests for a specific function or method. Fast, simple, and great for services or helpers.

$this->assertEquals(
    'cafe-racer', 
    SlugHelper::normalize('Café Racer')
);

🔹 Functional Tests #

Check how a feature behaves – like a form submission or controller logic.

$I->amOnPage('/contact');
$I->submitForm('#contact-form', [...]);
$I->see('Thank you');

🔹 Acceptance Tests #

Simulate a user flow in a real browser (e.g. login, create entry). These tests are great for workflows that span multiple parts of the system.

$I->amOnPage('/admin');
$I->fillField('Username', 'admin');
$I->click('Login');
$I->see('Dashboard');

🔹 Visual Regression Tests #

Use BackstopJS to compare screenshots of your site over time. If a layout shifts or an element disappears, it’ll flag it.

{
  label: "Homepage",
  url: "http://project.ddev.site/",
  selectors: ["document"],
  viewports: [{ width: 1280, height: 800 }]
}

Tools We Use #

  • Codeception – For unit, functional, and acceptance testing. Works well with Craft and Yii2. Uses PHPUnit under the hood.
  • BackstopJS – For catching CSS and layout regressions with screenshots.
  • DDEV – Docker-based environment to run all your tests reliably and consistently across the team.

What You Should Test on Every Craft Project #

You don’t need full coverage. But if you’re looking for where to start, this is a strong baseline:

  • ✅ A unit test for a helper (slug generator, price formatter, etc.)
  • ✅ A functional test for at least one form (contact, newsletter, etc.)
  • ✅ An acceptance test for login flow or critical path
  • ✅ A BackstopJS test of your homepage and a key interior template
  • ✅ A 404 page test – easy to set up and protects user experience

These are small to set up and pay off quickly — especially during Craft updates, plugin upgrades, or template refactors.


DDEV Makes Testing Easy #

With DDEV, you can run everything locally with ease. We have a custom command setup to make it simple to run our tests here at Viget.

.ddev/commands/web/codecept.sh

## Usage: codecept
## Description: Run codeception for this project
vendor/bin/codecept "$@"

Which can be run like this:

# Run PHP tests
$ ddev codecept run

Backstop can be run within DDEV with a little work, but we typically run it globally on our system:

# Run BackstopJS visual tests
$ backstop test

You can also run these in CI – and seeing tests pass or fail before you merge code is a huge boost in confidence.


Tips to Keep Your Tests Useful #

  • Use clean, repeatable test data (fixtures or factories)
  • Keep tests isolated – don’t rely on one test running before another
  • Enable visual output for failures (screenshots help a lot)
  • Focus on the fragile or critical features – not everything
  • Add tests to your pull request workflow when possible

This is less about perfection, and more about progress. A few well-placed tests will help your project stay stable as it grows.


Final Thoughts #

You don’t need a huge test suite to get value from testing. Just a few smart tests can save you time and give you confidence in your code.

Testing isn’t about slowing down development. It’s what allows us to move faster without sacrificing stability.

Get Started #

If you're looking to get started testing Craft CMS now, I've compiled a few resources that might be a good next step for you:

Craft CMS GitHub Repo (Craft Test Suite) Reference real-world tests and configuration from the core CMS team.

CraftQuest “Testing with Codeception” course Paid (but extremely valuable) video series on installing and configuring Codeception in Craft environments.

PHPUnit Testing in Laravel While not Craft-specific, I also highly recommend this series on Laracasts. It's a great deep dive into writing and refining good tests in PHP.

Getting Started With BackstopJS Clean walkthrough of setup with Docker, CI integration, and best practices to avoid flaky tests.


Further Reading #

The Craft 5 docs are notably missing a testing page. There's an internal debate whether or not to switch testing providers from Codeception to Pest. While that debate plays out, I recommend taking a look at the Craft 4 Testing docs for reference while getting testing setup.

Max Myers

Max is a Senior Platform Developer based in Michigan with extensive experience building robust e-commerce platforms and rebuilding vintage Kawasaki motorcycles.

More articles by Max

Related Articles