AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Moodle Phpunit Testing

skill-saadrahman01-claude-moodle-dev-moodle-phpunit-testing · by SaadRahman01

Use when writing, running, or debugging PHPUnit tests for Moodle plugins or core. Covers advanced_testcase, resetAfterTest, data generators, mocking $DB, testing events/tasks/external functions, and CLI invocation.

No reviews yet
0 installs
49 views
0.0% view→install

Install

$ agentstack add skill-saadrahman01-claude-moodle-dev-moodle-phpunit-testing

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-saadrahman01-claude-moodle-dev-moodle-phpunit-testing)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Moodle Phpunit Testing? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Moodle PHPUnit Testing

Overview

Moodle ships its own PHPUnit harness with test bootstrap, transactional resets, and data generators. Tests live in /tests/_test.php and extend advanced_testcase. Never call parent::setUp() for DB cleanup — use $this->resetAfterTest().

When to Use

  • Writing unit/integration tests for any Moodle plugin or core API
  • Debugging test failures (Database was modified errors, isolation issues)
  • Adding a data generator (tests/generator/lib.php)
  • Testing events, scheduled tasks, ad-hoc tasks, external functions
  • Setting up CI for Moodle test suites

Skip when: writing Behat acceptance tests (use moodle-behat-testing).

First-time setup

php admin/tool/phpunit/cli/init.php       # writes phpunit.xml + initializes test DB
vendor/bin/phpunit --testsuite local_example_testsuite

phpunit.xml is regenerated by init.php — never hand-edit. Re-run after installing a new plugin.

Test class skeleton

resetAfterTest();
        $generator = self::getDataGenerator();
        $course = $generator->create_course();
        $user = $generator->create_user();

        $manager = new manager();
        $id = $manager->create_item($course->id, $user->id, 'hello');

        global $DB;
        $row = $DB->get_record('local_example_items', ['id' => $id], '*', MUST_EXIST);
        $this->assertSame('hello', $row->name);
    }
}

Key rules:

  • File name: _test.php, class: _test
  • final class (Moodle policy since 4.2)
  • @covers annotation required by Moodle CS
  • @group enables --group filtering
  • void return type on test methods, : void on setUp
  • self:: (not $this->) for static methods like getDataGenerator()

Data generators

Plugin generator at tests/generator/lib.php:

 0,
            'userid'     => $USER->id,
            'name'       => 'Item ' . random_string(8),
            'timecreated'=> time(),
        ];
        $record = (object)array_merge($defaults, $record);
        $record->id = $DB->insert_record('local_example_items', $record);
        return $record;
    }
}

Use:

$gen = self::getDataGenerator()->get_plugin_generator('local_example');
$item = $gen->create_item(['name' => 'test']);

Activity module generator extends testing_module_generator and implements create_instance().

Common patterns

Test an event

$sink = $this->redirectEvents();
$manager->do_thing();
$events = $sink->get_events();
$sink->close();
$this->assertCount(1, $events);
$this->assertInstanceOf(\local_example\event\thing_done::class, $events[0]);

Test an email

$sink = $this->redirectEmails();
$manager->notify($user);
$messages = $sink->get_messages();
$this->assertSame($user->email, $messages[0]->to);

Test a scheduled task

$task = new \local_example\task\cleanup();
$task->execute();
// assert side effects

Test an external (web service) function

$this->setUser($user);
$result = \local_example\external\get_items::execute($courseid);
$result = \core_external\external_api::clean_returnvalue(
    \local_example\external\get_items::execute_returns(),
    $result
);
$this->assertCount(2, $result);

clean_returnvalue is mandatory — catches schema mismatches.

Test an ad-hoc task

\core\task\manager::queue_adhoc_task(new \local_example\task\send_report());
$this->runAdhocTasks(\local_example\task\send_report::class);

Login as a user

$user = $this->getDataGenerator()->create_user();
$this->setUser($user);             // sets $USER global
$this->setAdminUser();             // shortcut
$this->setGuestUser();

Time-travel

$this->mock_clock_with_frozen(1700000000);    // Moodle 4.4+
// or in older versions, manually set timecreated/timemodified

Running tests

# Single suite
vendor/bin/phpunit --testsuite local_example_testsuite

# Single file
vendor/bin/phpunit local/example/tests/manager_test.php

# Single method
vendor/bin/phpunit --filter test_create_item local/example/tests/manager_test.php

# By group
vendor/bin/phpunit --group local_example

# Coverage (requires xdebug or pcov)
vendor/bin/phpunit --coverage-html coverage/ local/example/tests

Test database

  • Separate DB defined in config.php: $CFG->phpunit_prefix = 'phpu_';
  • Reset between tests via transactions — $this->resetAfterTest() enables it
  • Schema drift error: re-run php admin/tool/phpunit/cli/init.php
  • "Database was modified" failure means a test mutated DB without resetAfterTest()

Mocking

Moodle prefers integration tests with the real test DB over mocking $DB. When you must mock:

$mockDB = $this->createMock(\moodle_database::class);
$mockDB->method('get_record')->willReturn((object)['id' => 1]);
// inject via DI, never replace global

Avoid replacing the global $DB — breaks isolation.

Common Mistakes

| Mistake | Fix | |---------|-----| | Forgetting $this->resetAfterTest() | Add at start of every DB-touching test | | Class not final | Add final (Moodle 4.2+ policy) | | Missing @covers | Add @covers \Fully\Qualified\Class | | Hand-editing phpunit.xml | Re-run admin/tool/phpunit/cli/init.php | | Using parent::setUp() to reset DB | Use resetAfterTest() instead | | Skipping clean_returnvalue on external fn | Always wrap external returns to catch schema bugs | | $this->getDataGenerator() (instance) | Moodle prefers self::getDataGenerator() (static) | | Asserting time with time() | Use mock_clock_with_frozen or compare with tolerance |

CI snippet (GitHub Actions)

- name: PHPUnit
  run: |
    php admin/tool/phpunit/cli/init.php
    vendor/bin/phpunit --testsuite ${{ matrix.suite }}

References

  • PHPUnit in Moodle: https://moodledev.io/general/development/tools/phpunit
  • Data generators: https://moodledev.io/docs/apis/subsystems/testing/generators
  • Test writing guide: https://moodledev.io/general/development/policies/testing
  • Coverage: https://moodledev.io/general/development/tools/phpunit#code-coverage

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.