Behat testing in Moodle

Tom Dickman

What is 

Business Readable, Domain Specific Language

Feature: Some terse yet descriptive text of what is desired
  In order to realize a named business value
  As an explicit system actor
  I want to gain some beneficial outcome which furthers the goal

  Additional text...

  Scenario: Some determinable business situation
    Given some precondition
    And some other precondition
    When some action by the actor
    And some other action
    And yet another action
    Then some testable outcome is achieved
    And something else we can check happens too

  Scenario: A different situation

What is 


Feature: Delete custom licenses
  In order to manage custom licenses
  As an admin
  I need to be able to delete custom licenses

  Scenario: I can delete a custom license
    Given I log in as "admin"
    And I navigate to "Licences > Manage licences" in site administration
    And I click on "Create licence" "link"
    And I set the following fields to these values:
    | shortname      | MIT                                 |
    | fullname       | MIT Licence                         |
    | source         | |
    | version[day]   | 1                                   |
    | version[month] | March                               |
    | version[year]  | 2019                                |
    And I press "Save changes"
    And I click on "Delete" "icon" in the "MIT" "table_row"
    And I click on "Save changes" "button" in the "Delete licence" "dialogue"
    Then I should not see "MIT Licence" in the "manage-licenses" "table"
    And I log out

What is Mink?

  • Control the browser
  • Traverse pages
  • Manipulate pages
  • Interact with pages
// Choose a Mink driver.
$driver = new \Behat\Mink\Driver\Selenium2Driver('firefox');
$session = new \Behat\Mink\Session($driver);
// Start the session
// Manipulate the browser

Abstraction layer between browser emulators

Structure - Background

  Given the following "users" exist:
    | username | firstname | lastname | email                | idnumber |
    | student1 | Student   | X        | | S1       |
  And the following "categories" exist:
    | name        | category | idnumber |
    | Category 1  | 0        | CAT1     |
  And the following "courses" exist:
    | fullname | shortname | category | startdate         | enddate         |
    | Course 1 | C1        | 0        | ##1 month ago##   | ##15 days ago## |
  And the following "course enrolments" exist:
    | user     | course | role    |
    | student1 | C1     | student |

Structure - Scenario

Context > Event > Outcome

Scenario: Description of the scenario
    Given some context
    When some event
    Then outcome

Real Example

Context > Event > Outcome

  Scenario: I cannot delete a standard Moodle license
    Given I log in as "admin"
    And I navigate to "Licences > Manage licences" in site administration
    Then I should see "Licence not specified" in the "unknown" "table_row"
    But I should not see "Delete" in the "unknown" "table_row"

Use of And and But to link and exclude Events

How does Moodle use behat?

$CFG->behat_wwwroot   = 'http://webserver';
$CFG->behat_dataroot  = '/var/www/behatdata';
$CFG->behat_prefix = 'b_';
$CFG->behat_profiles = array(
    'default' => array(
        'browser' => 'firefox',
        'wd_host' => 'http://selenium:4444/wd/hub',
$CFG->behat_faildump_path = '/var/www/behatfaildumps';


// Path to screenshots is set in config.php file
$CFG->behat_faildump_path = '/var/www/behatfaildumps';

To screenshot every step, add this

to your Behat test run command

docker cp moodledocker_webserver_1:/var/www/behatfaildumps/. /path/to/target/directory/.

Transfer Docker Moodle screen dumps

to local directory


  Scenario: View past courses
    Given I log in as "student1"
    And I click on "All" "button" in the "Course overview" "block"
    When I click on "Past" "link" in the "Course overview" "block"
    Then I should see "Course 1" in the "Course overview" "block"
    And I should not see "Course 2" in the "Course overview" "block"
    And I should not see "Course 3" in the "Course overview" "block"
    And I should not see "Course 4" in the "Course overview" "block"
    And I should not see "Course 5" in the "Course overview" "block"
    And I log out

  Scenario: List display  persistence
    Given I log in as "student1"
    And I click on "Display drop-down menu" "button" in the "Course overview" "block"
    And I click on "List" "link" in the "Course overview" "block"
    And I reload the page
    Then I should see "List" in the "Course overview" "block"
    And "[data-display='list']" "css_element" in the "Course overview" "block" should be visible


Not as Good

How to select things



List of XPATHs

'icon' => <<<XPATH
.//*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') and ( contains(normalize-space(@title), %locator%))]


Then "Default" "icon" should exist in the "public" "table_row"


vendor/bin/behat --config /var/www/behatdata/behatrun/behat/behat.yml --tags block_myoverview
@block @block_myoverview @javascript
Feature: The my overview block allows users to easily access their courses
  In order to enable the my overview block in a course
  As a student
  I can add the my overview block to my dashboard


Setup Requirements - Manual

  • A Moodle instance running on a webserver which you can connect to
  • Configurations in the config.php file in the code base for server
$CFG->behat_prefix = 'behat_';
$CFG->behat_dataroot = '/var/www/moodledata_behat';
  • Selenium installed and running in the server
  • Behat install from Composer (handled by the admin CLI tool)
java -jar /path/to/your/selenium/server/selenium-server-standalone-3.13.0.jar -port 4444
  • Latest Chrome driver installed
sudo mv chromedriver /usr/bin/chromedriver
sudo chown root:root /usr/bin/chromedriver
sudo chmod +x /usr/bin/chromedriver
php admin/tool/behat/cli/init.php
  • More info in the Moodle Docs 'Running acceptence test'

Setup Requirements - Docker

  • Docker and Docker Compose installed
  • Moodle HQ docker repo cloned 
  • Path to Moodle code setup and database chosen
  • Run the tests
bin/moodle-docker-compose up -d
bin/moodle-docker-compose exec webserver php admin/tool/behat/cli/run.php

bin/moodle-docker-compose down #Close the containers down

AKA 'The Easy Way'

git clone
export MOODLE_DOCKER_WWWROOT=/path/to/moodle/code
export MOODLE_DOCKER_DB=pgsql
  • Template config.php file copied from repo
cp config.docker-template.php $MOODLE_DOCKER_WWWROOT/config.php
  • Start the containers

Cloning Repos with Submodules


Clone the project:

cd monash
git submodule init
git submodule update --recursive

Install the submodules:

git clone monash

Check mod/hvp and tool/etl:

cd mod/hvp
git submodule update --init --recursive

Updating a submodule

Change into submodule folder:

git checkout branch
git pull

Update submodule to the latest changes on the target branch:

cd mod/name

Commit updated submodule hash:

cd ../.. #Change back to parent repo
git add mod/name
git diff --cached #Check changes
git commit -m 'WR#111111: Update submodule mod_name'

Note: this branch should reflect that in .gitmodules

Debug Unit Tests with PHPStorm

Inside you Moodle Docker containers, you can add environment variables to debug using PHPStorm

docker exec -it moodledocker_webserver_1 bash #Enter a bash terminal inside the container
export XDEBUG_CONFIG="remote_host= remote_port=9000"
export PHP_IDE_CONFIG="serverName=webserver"

Now set break points in PHPStorm for the test you want to debug, and ruun tests unsing Moodle Docker instructions

Inside PHP Storm configure the Server to listen to correct folder

  • Ctrl + Alt + S to get to Settings
  • Languages & Frameworks > PHP > Servers
  • Add a new server
  • Host: webserver
  • Port: 80
  • Beside the path to your Moodle code base set Absolute path on the server to /var/www/moodledata
  • Apply the changes


Useful links

Behat testing

By Tom Dickman

Behat testing

An introduction to Behat and using it in Moodle development

  • 181
Loading comments...

More from Tom Dickman