Skip to main content

Test suite overview

Semantic MediaWiki uses PHPUnit for automated testing. Tests are divided into two main categories:
  • Unit tests — verify isolated classes and components without a database or external services
  • Integration tests — verify the interplay between components by running a full MediaWiki stack with a live database connection
About 80% of CI running time is spent on integration tests, since they run a full cycle: parsing, storing, reading, and HTML generation.
All tests are required to pass before changes can be merged. New functionality must be accompanied by both unit and integration tests.

Test directory structure

tests/
└── phpunit/
    ├── Benchmark/       # Benchmark tests (excluded from standard CI runs)
    ├── Fixtures/        # Fixed data and schemata shared across tests
    ├── Integration/     # Integration tests (requires DB + MediaWiki stack)
    │   └── JSONScript/  # JSON-based integration test scenarios
    ├── Structure/       # Structural verification tests
    └── (unit tests)     # Unit test classes, mirroring src/ layout

Test suites

Test suites are defined in phpunit.xml.dist. Each suite groups related tests:
Suite nameContents
semantic-mediawiki-unitAll unit test classes across src/
semantic-mediawiki-data-modelData model unit tests
semantic-mediawiki-integrationIntegration tests (excluding import tests)
semantic-mediawiki-importMediaWiki import integration tests
semantic-mediawiki-structureStructural verification tests
semantic-mediawiki-checkPHPUnit check runner
semantic-mediawiki-benchmarkBenchmark tests (excluded from standard runs)

Running tests

Via Make (from the host)

The simplest way to run the full suite is:
make composer-test
This runs lint, PHPCS, and all PHPUnit suites — the same sequence as CI. To run a specific suite via Make:
make composer-test COMPOSER_PARAMS="-- --testsuite=semantic-mediawiki-unit"

Inside the container

Open a shell with make bash, then run Composer scripts directly:
composer phpunit:unit

Via docker exec (from the host)

Run specific suites or classes without entering the container:
# Unit tests
docker exec semanticmediawiki-mysql-wiki-1 bash -c \
  "cd /var/www/html/extensions/SemanticMediaWiki && composer phpunit:unit -- --no-coverage"

# Integration tests
docker exec semanticmediawiki-mysql-wiki-1 bash -c \
  "cd /var/www/html/extensions/SemanticMediaWiki && composer phpunit:integration -- --no-coverage"

# Single test class
docker exec semanticmediawiki-mysql-wiki-1 bash -c \
  "cd /var/www/html/extensions/SemanticMediaWiki && composer phpunit -- --no-coverage --filter 'MyTestClassName'"
The container name follows the pattern semanticmediawiki-<DB_TYPE>-wiki-1. With the default mysql database type, it is semanticmediawiki-mysql-wiki-1.

Writing unit tests

Unit tests live in tests/phpunit/ in directories that mirror the src/ layout. Follow the arrange, act, assert pattern and avoid hidden expectations or deep inheritance.
  • Use PHPUnit\Framework\TestCase as the base class for pure unit tests
  • Use MwDBaseUnitTestCase only when a MediaWiki database connection is required
  • Avoid MediaWikiTestCase — it binds tests tightly to the MediaWiki test environment
  • Keep each test class focused on a single unit of behaviour
  • Use dependency injection in the class under test; inject test doubles (mocks, stubs) in the test
<?php

namespace SMW\Tests\MyComponent;

use PHPUnit\Framework\TestCase;
use SMW\MyComponent\MyClass;

/**
 * @covers \SMW\MyComponent\MyClass
 */
class MyClassTest extends TestCase {

    public function testDoSomethingReturnsExpectedValue(): void {
        // Arrange
        $subject = new MyClass();

        // Act
        $result = $subject->doSomething( 'input' );

        // Assert
        $this->assertSame( 'expected', $result );
    }
}
Add your test file to the semantic-mediawiki-unit testsuite in phpunit.xml.dist if it lives in a directory not already listed.

Writing integration tests

Integration tests live in tests/phpunit/Integration/ and require a live MediaWiki and database stack. They complement unit tests by verifying that components behave correctly together. A practical workflow for writing an integration test:
1

Reproduce the behaviour

Find a minimal wiki-text scenario that demonstrates the expected (or broken) behaviour. Wiki-text is the easiest way to drive SMW’s full parsing and storage pipeline.
2

Write a failing test

Encode the scenario as a JSONScript test file (see below) and confirm it fails before applying any fix.
3

Apply the fix

Implement the change in the source code.
4

Confirm the test passes

Run the test again. Then run the full integration suite to ensure no regressions were introduced.

JSONScript integration tests

The preferred format for integration tests in Semantic MediaWiki is JSONScript — a JSON-based abstraction layer that lets you write test scenarios using wiki-text markup without needing to learn PHPUnit or internal MediaWiki objects. JSONScript files live in tests/phpunit/Integration/JSONScript/. Each file defines:
  • Pages to create (with wiki-text content)
  • Assertions to run against parsed output, stored data, or query results
A minimal JSONScript test looks like:
{
  "description": "Test that a property annotation is stored correctly",
  "setup": [
    {
      "namespace": "NS_MAIN",
      "name": "ExamplePage",
      "contents": "[[Has population::12345]]"
    }
  ],
  "tests": [
    {
      "type": "parser",
      "about": "#01 Annotation is present in semantic data",
      "subject": "ExamplePage",
      "assert-store": {
        "semantic-data": {
          "strict-mode": true,
          "Has population": [ { "type": "_num", "value": "12345" } ]
        }
      }
    }
  ]
}
JSONScript tests are intentionally readable by non-developers. When writing tests for a bug fix, use wiki-text that directly mirrors the user’s reported scenario.

Code coverage

Generate a coverage report in Clover XML format:
docker exec semanticmediawiki-mysql-wiki-1 bash -c \
  "cd /var/www/html/extensions/SemanticMediaWiki && composer phpunit-coverage"
Coverage results are written to coverage/php/coverage.xml. The project publishes coverage data to Codecov on every CI run.

Continuous integration

CI runs on GitHub Actions using the same Docker and Makefile setup as local development. The workflow is defined in .github/workflows/main.yml. The CI matrix covers:
MediaWikiPHPDatabaseStatus
1.438.2MariaDB 11.2Stable (includes coverage report)
1.438.3MariaDB 11.2Stable
1.448.3MariaDB 11.2Experimental
1.448.3MariaDB 11.8Experimental
1.458.4MariaDB 11.8Experimental
CI runs make ci, which calls make install followed by composer test and npm test.
Pull requests must be green on all voting CI jobs before they can be merged. Check the Actions tab on your PR to monitor job status.

Build docs developers (and LLMs) love