Training · Introductory

Getting Started with PHPUnit

Anchoring automated tests in your project: structure, environment, CI, and test data

You want to test automatically, but not a single test runs in your project yet? Bring PHPUnit into your project: with a structure that grows with it, reliably on every machine and in CI, and without real data.

  • Scope 4 sessions of 2 hours
  • Format Live via Zoom
  • Language German
  • Price per person €799

What it is about

The first test is written quickly, or generated by an agent. For it to be of use in day-to-day work, your project needs more: a place for tests, an order that still holds with a thousand tests, and a test run that delivers the same result on every machine. Above all, it needs a test run that happens automatically before a change reaches the main branch.

In this training you bring PHPUnit into your project. It leaves aside how to write tests and deals with what your project needs for tests to become part of everyday work. You decide where which tests live and how you select what runs. You run your tests in a container, with the same PHP version as in production, and in CI, where a red test blocks the merge.

The last session belongs to data. Tests against the production database are tempting and dangerous: they can delete data, send emails to real customers, and take personal data to places where it does not belong. You set up a test database with synthetic data and make sure, technically, that no test reaches production.

How you work in this training

You work on everything that is needed around the tests, but you do not write any tests yourself. They already exist: in the exercise project as a prepared collection, and in your own project generated by an agent or taken over from the exercise project.

If you bring a project of your own and are allowed to work on it, you work on it; otherwise you use the exercise project. That is a small PHP application that has grown over time, without tests, with a database and email sending, and all demonstrations use it. It comes with a Containerfile with PHP 8.5, Composer, and the extensions of the application, a Compose file with a database and a mail catcher, and templates for GitHub Actions and GitLab CI.

Every decision is justified, so that you end up with a project whose setup your team understands rather than with a template to copy.

Programme

  1. Session 1: Bringing PHPUnit into the project

    What does your project need for the first test to run?

    • Installing with Composer as a development dependency, the PHAR as an alternative, and autoload-dev for the test code
    • Which PHPUnit version fits which PHP version, and how long it is supported
    • Generating phpunit.xml with --generate-configuration, explaining it line by line, and thinning it out together, with a reason for every deletion, including that of warnWhenPhpIsNotConfiguredForDevelopment
    • Configuring PHP for development: error reporting and assertions
    • The first run with prepared tests: --testdox, --filter, IDE integration, and how to read success, failure, error, warnings, and notices

    Result: PHPUnit running in your project or in the exercise project, with a configuration in which every remaining line has a reason

  2. Session 2: Order in the test suite

    Where does which test live, and how do you select what runs?

    • Directories separated by test size (tests/unit, tests/integration, tests/end-to-end) or following src/, and what follows from each
    • Namespaces and autoload-dev, naming conventions for test classes and test methods
    • Test suites and groups in phpunit.xml, selecting them with --testsuite and --group, and declaring with <source> what belongs to the production code
    • Where shared test code belongs: helper classes, fakes, builders
    • One entry point for all runs: Composer scripts such as composer test and composer test:unit

    Result: The unsorted test collection of the exercise project in a defined structure, with test suites, conventions, and Composer scripts as the entry point for every run

  3. Session 3: When and where tests run

    Does your test suite run the same everywhere, and who notices when it is red?

    • Where: on the host, in a container, in CI, and why the PHP version and the extensions should match those of production
    • Containers for tests: Containerfile, mounting the code, the Composer cache, Docker and Podman
    • When: during development in the IDE, before the commit (Git hooks as an option, not as a safety net), in CI on every push and before the merge
    • The exit code as the verdict: why CI does not read the output
    • CI configuration for GitHub Actions and GitLab CI that only allows a merge after a green run
    • Outlook: what should run when, once the suite grows

    Result: A test run in a container with the same PHP version as in production and in CI on every push, and a red test committed on purpose that shows you the merge is blocked

  4. Session 4: Tests and data

    Why does the production database have no place in your tests?

    • Data loss: tests create, change, and delete data, fixtures overwrite existing records, cleaning up empties tables
    • Side effects: emails to real customers, calls to real payment services
    • GDPR: why personal data in test and development environments is a problem, and why an “anonymised” copy is often only pseudonymised
    • Not reproducible: a test that depends on changing real data is green today and red tomorrow
    • The alternative: a test database of its own in a container, the schema from the migrations, synthetic test data from fixtures and builders, and a fresh state for every run
    • Technical locks: separate credentials, environment variables of their own for tests, a bootstrap that aborts when the connection points to production, and a mail catcher instead of real delivery

    Result: A test database in a container with synthetic data, and a bootstrap that aborts a run with the credentials of the “production database” of the exercise project before a single test is executed

Who it is for

  • PHP teams who want to start with automated tests
  • Developers whose tests are only green on one machine or do not run in CI at all
  • Tech leads who create the foundation for automated tests for their team

Prerequisites

  • Experience with PHP and Composer
  • Ideally a project of your own that you are allowed to work on during the training; otherwise you work on the exercise project
  • For the exercises: PHP 8.5, Composer, Git, Docker or Podman, and an account with GitHub or GitLab
  • No prior knowledge of testing is required: “Understanding Testing”, which you can attend in parallel, teaches the terms

What you take away

  • You get tests running in your project and know why they are set up the way they are
  • You give your tests an order and select specifically which of them run
  • You run your tests with the same result on every machine, in a container, and in CI
  • You make sure that a red test keeps a change out of the main branch
  • You test with synthetic data and lock your tests out of production technically

Your trainer

Sebastian Bergmann answering a participant's question at the laptop
Photo: Filip Kovačević

Sebastian Bergmann is the creator of PHPUnit, the de facto standard for testing PHP code. Since 2001 he has been helping teams build maintainable and well-tested software as a consultant and trainer, since 2009 as a co-founder of thePHP.cc.

You learn how PHPUnit gets into your project first-hand: from the person who wrote it.