Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Jenkins Integration Testing for Plugin Development: Difference between revisions

From MoodleDocs
m (Text replacement - "<code>" to "<syntaxhighlight lang="php">")
m (Text replacement - "</code>" to "</syntaxhighlight>")
 
Line 20: Line 20:
<div style='padding-left: 30pt'>
<div style='padding-left: 30pt'>
<syntaxhighlight lang="php">sudo apt-get install postgresql<br />
<syntaxhighlight lang="php">sudo apt-get install postgresql<br />
sudo apt-get install php5-cli php-pear php5-curl php5-pgsql php5-gd php5-intl</code>
sudo apt-get install php5-cli php-pear php5-curl php5-pgsql php5-gd php5-intl</syntaxhighlight>
</div>
</div>


Line 28: Line 28:
</ul>
</ul>
<div style='padding-left: 30pt'>
<div style='padding-left: 30pt'>
<syntaxhighlight lang="php">sudo apt-get install git</code><br>
<syntaxhighlight lang="php">sudo apt-get install git</syntaxhighlight><br>
</div>
</div>
<ul>
<ul>
Line 34: Line 34:
</ul>
</ul>
<div style='padding-left: 30pt'>
<div style='padding-left: 30pt'>
<syntaxhighlight lang="php">sudo apt-get install xvfb firefox</code><br>
<syntaxhighlight lang="php">sudo apt-get install xvfb firefox</syntaxhighlight><br>
</div>
</div>
<ul>
<ul>
Line 44: Line 44:
<p>
<p>
<ul>
<ul>
<li>Configuration of the init script can be found at <syntaxhighlight lang="php">/etc/default/jenkins</code>. It is not strictly necessary to change anything here, but we set <syntaxhighlight lang="php">HTTP_PORT=-1</code> and added <syntaxhighlight lang="php">HTTPS_PORT=8998</code> to make the server only via HTTPS.  In that case, you also need to add <syntaxhighlight lang="php">--httpsPort=$HTTPS_PORT</code> to the <syntaxhighlight lang="php">JENKINS_ARGS</code> variable and restart Jenkins.</li>
<li>Configuration of the init script can be found at <syntaxhighlight lang="php">/etc/default/jenkins</syntaxhighlight>. It is not strictly necessary to change anything here, but we set <syntaxhighlight lang="php">HTTP_PORT=-1</syntaxhighlight> and added <syntaxhighlight lang="php">HTTPS_PORT=8998</syntaxhighlight> to make the server only via HTTPS.  In that case, you also need to add <syntaxhighlight lang="php">--httpsPort=$HTTPS_PORT</syntaxhighlight> to the <syntaxhighlight lang="php">JENKINS_ARGS</syntaxhighlight> variable and restart Jenkins.</li>
<li>Furthermore, Moodle requires a <syntaxhighlight lang="php">config.php</code> which is not contained in a repository. Therefore, configuration files need to be stored at a separate location, from where they can be pulled in. For our scenario, we stored the configuration file at <syntaxhighlight lang="php">$JENKINS_HOME/moodle-config/config_$target.php</code>, where $target indicates a Moodle branch to build against (e.g., /var/lib/jenkins/moodle-config/config_MOODLE_28_STABLE.php).</li>
<li>Furthermore, Moodle requires a <syntaxhighlight lang="php">config.php</syntaxhighlight> which is not contained in a repository. Therefore, configuration files need to be stored at a separate location, from where they can be pulled in. For our scenario, we stored the configuration file at <syntaxhighlight lang="php">$JENKINS_HOME/moodle-config/config_$target.php</syntaxhighlight>, where $target indicates a Moodle branch to build against (e.g., /var/lib/jenkins/moodle-config/config_MOODLE_28_STABLE.php).</li>
</ul>
</ul>
</p>
</p>
Line 71: Line 71:
<li>Git</li>
<li>Git</li>
<ul>
<ul>
<li>Add a git installation. Choose an arbitrary name and set the path to <syntaxhighlight lang="php">git</code> </li>
<li>Add a git installation. Choose an arbitrary name and set the path to <syntaxhighlight lang="php">git</syntaxhighlight> </li>
</ul>
</ul>
<li>Git plugin</li>
<li>Git plugin</li>
<ul>
<ul>
<li>Set the fields <syntaxhighlight lang="php">Global Config user.name Value</code> and <syntaxhighlight lang="php">Global Config user.email Value</code>. In our case, we set those values to account and e-mail of a technical GitHub account that is only used by the Jenkins server</li>
<li>Set the fields <syntaxhighlight lang="php">Global Config user.name Value</syntaxhighlight> and <syntaxhighlight lang="php">Global Config user.email Value</syntaxhighlight>. In our case, we set those values to account and e-mail of a technical GitHub account that is only used by the Jenkins server</li>
</ul>
</ul>
</ul>
</ul>
Line 90: Line 90:
<li>Source Code management:</li>
<li>Source Code management:</li>
<ul>
<ul>
<li>Choose <syntaxhighlight lang="php">Multiple SCMs</code>.</li>
<li>Choose <syntaxhighlight lang="php">Multiple SCMs</syntaxhighlight>.</li>
<li>Use <syntaxhighlight lang="php">Add SCM</code> to add two git repositories.</li>
<li>Use <syntaxhighlight lang="php">Add SCM</syntaxhighlight> to add two git repositories.</li>
<li>For the first repository,</li>
<li>For the first repository,</li>
<ul>
<ul>
<li>set the URL to <syntaxhighlight lang="php">git://git.moodle.org/moodle.git</code>.</li>
<li>set the URL to <syntaxhighlight lang="php">git://git.moodle.org/moodle.git</syntaxhighlight>.</li>
<li>Since this is a public repository, credentials are not required.</li>
<li>Since this is a public repository, credentials are not required.</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Advanced clone behaviours</code> and check the option <syntaxhighlight lang="php">Shallow clone</code>.</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Advanced clone behaviours</syntaxhighlight> and check the option <syntaxhighlight lang="php">Shallow clone</syntaxhighlight>.</li>
</ul>
</ul>
<li>For the second repository,</li>
<li>For the second repository,</li>
Line 102: Line 102:
<li>set the URL to the URL of your git repository.</li>
<li>set the URL to the URL of your git repository.</li>
<li>If this is a private repository, add credentials for accessing it (consider SSH authentication!)</li>
<li>If this is a private repository, add credentials for accessing it (consider SSH authentication!)</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Check out to a sub-directory</code> and, for <syntaxhighlight lang="php">Local subdirectory for repo</code>, enter the path where you would deploy your module in a usual Moodle installation (e.g., <syntaxhighlight lang="php">mod/yourmodule</code>), without surrounding slashes.</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Check out to a sub-directory</syntaxhighlight> and, for <syntaxhighlight lang="php">Local subdirectory for repo</syntaxhighlight>, enter the path where you would deploy your module in a usual Moodle installation (e.g., <syntaxhighlight lang="php">mod/yourmodule</syntaxhighlight>), without surrounding slashes.</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Advanced clone behaviours</code> and check the option <syntaxhighlight lang="php">Shallow clone</code>.</li>
<li>Under Additional Behaviours, add <syntaxhighlight lang="php">Advanced clone behaviours</syntaxhighlight> and check the option <syntaxhighlight lang="php">Shallow clone</syntaxhighlight>.</li>
</ul>
</ul>
</ul>
</ul>
Line 109: Line 109:
<ul>
<ul>
<li>Check Build when a change is pushed to GitHub.</li>
<li>Check Build when a change is pushed to GitHub.</li>
<li>Check Poll SCM. As a schedule, define e.g. <syntaxhighlight lang="php">H/5 * * * *</code> for polling every five minutes.</li>
<li>Check Poll SCM. As a schedule, define e.g. <syntaxhighlight lang="php">H/5 * * * *</syntaxhighlight> for polling every five minutes.</li>
</ul>
</ul>
<li>Configuration Matrix:</li>
<li>Configuration Matrix:</li>
Line 115: Line 115:
<li>Add two user-defined axes.</li>
<li>Add two user-defined axes.</li>
<ul>
<ul>
<li>For the first axis, set name to <syntaxhighlight lang="php">target</code> and values to <syntaxhighlight lang="php">MOODLE_28_STABLE MOODLE_27_STABLE</code>. These values correspond to the branch names used in the Moodle repository.</li>
<li>For the first axis, set name to <syntaxhighlight lang="php">target</syntaxhighlight> and values to <syntaxhighlight lang="php">MOODLE_28_STABLE MOODLE_27_STABLE</syntaxhighlight>. These values correspond to the branch names used in the Moodle repository.</li>
<li>For the second axis, set name to <syntaxhighlight lang="php">testtype</code> and values to <syntaxhighlight lang="php">UnitTest BehatTest</code>. These are used later on to perform different test types - be patient :).</li>
<li>For the second axis, set name to <syntaxhighlight lang="php">testtype</syntaxhighlight> and values to <syntaxhighlight lang="php">UnitTest BehatTest</syntaxhighlight>. These are used later on to perform different test types - be patient :).</li>
</ul>
</ul>
<li>Check Run each configuration sequentially.</li>
<li>Check Run each configuration sequentially.</li>
Line 128: Line 128:
<li>Add Execute shell</li>
<li>Add Execute shell</li>
<ul>
<ul>
<li>Enter the following commands (Note that we use <syntaxhighlight lang="php">$target</code> here, which always holds the current value of <syntaxhighlight lang="php">target</code> in a particular configuration. Furthermore, this copies an appropriate config.php file into the current workspace):</li>
<li>Enter the following commands (Note that we use <syntaxhighlight lang="php">$target</syntaxhighlight> here, which always holds the current value of <syntaxhighlight lang="php">target</syntaxhighlight> in a particular configuration. Furthermore, this copies an appropriate config.php file into the current workspace):</li>
</ul>
</ul>
</ul>
</ul>
Line 138: Line 138:
curl -s https://getcomposer.org/installer | php;<br />
curl -s https://getcomposer.org/installer | php;<br />
php composer.phar install --prefer-source;<br />
php composer.phar install --prefer-source;<br />
cp $JENKINS_HOME/moodle-config/config_$target.php config.php;</code>
cp $JENKINS_HOME/moodle-config/config_$target.php config.php;</syntaxhighlight>
</div>
</div>


Line 145: Line 145:
<li>Add Conditional step (single)</li>
<li>Add Conditional step (single)</li>
<ul>
<ul>
<li>Run?: <syntaxhighlight lang="php">Strings match</code>, String 1: <syntaxhighlight lang="php">${testtype}</code>, String 2: <syntaxhighlight lang="php">UnitTest</code> (the motivation is that this command is only run for unit tests. In contrast, the other step will be for behat acceptance tests).</li>
<li>Run?: <syntaxhighlight lang="php">Strings match</syntaxhighlight>, String 1: <syntaxhighlight lang="php">${testtype}</syntaxhighlight>, String 2: <syntaxhighlight lang="php">UnitTest</syntaxhighlight> (the motivation is that this command is only run for unit tests. In contrast, the other step will be for behat acceptance tests).</li>
<li>Builder: <syntaxhighlight lang="php">Execute shell</code></li>
<li>Builder: <syntaxhighlight lang="php">Execute shell</syntaxhighlight></li>
<li>Enter the following commands:</li>
<li>Enter the following commands:</li>
</ul>
</ul>
Line 154: Line 154:
<syntaxhighlight lang="php">mkdir -p ../moodledata/phpunit;<br />
<syntaxhighlight lang="php">mkdir -p ../moodledata/phpunit;<br />
php admin/tool/phpunit/cli/init.php;<br />
php admin/tool/phpunit/cli/init.php;<br />
vendor/bin/phpunit --log-junit results/phpunit/phpunit.xml --group mod_ratingallocate || true;</code>
vendor/bin/phpunit --log-junit results/phpunit/phpunit.xml --group mod_ratingallocate || true;</syntaxhighlight>
</div>
</div>


Line 161: Line 161:
<li>Add Conditional step (single)</li>
<li>Add Conditional step (single)</li>
<ul>
<ul>
<li>Run?: <syntaxhighlight lang="php">Strings match</code>, String 1: <syntaxhighlight lang="php">${testtype}</code>, String 2: <syntaxhighlight lang="php">BehatTest</code>.</li>
<li>Run?: <syntaxhighlight lang="php">Strings match</syntaxhighlight>, String 1: <syntaxhighlight lang="php">${testtype}</syntaxhighlight>, String 2: <syntaxhighlight lang="php">BehatTest</syntaxhighlight>.</li>
<li>Builder: <syntaxhighlight lang="php">Execute shell</code></li>
<li>Builder: <syntaxhighlight lang="php">Execute shell</syntaxhighlight></li>
<li>Enter the following commands (replace <syntaxhighlight lang="php">[SeleniumVersion]</code>, <syntaxhighlight lang="php">[ModuleTag]</code>, and <syntaxhighlight lang="php">[JobName]</code> accordingly):</li>
<li>Enter the following commands (replace <syntaxhighlight lang="php">[SeleniumVersion]</syntaxhighlight>, <syntaxhighlight lang="php">[ModuleTag]</syntaxhighlight>, and <syntaxhighlight lang="php">[JobName]</syntaxhighlight> accordingly):</li>
</ul>
</ul>
</ul>
</ul>
Line 176: Line 176:
vendor/bin/behat --config /var/lib/jenkins/jobs/[JobName]/workspace/target/moodledata/behat/behat/behat.yml --tags '@[ModuleTag]' --format moodle_progress,junit --out ,behatlog || true;<br />
vendor/bin/behat --config /var/lib/jenkins/jobs/[JobName]/workspace/target/moodledata/behat/behat/behat.yml --tags '@[ModuleTag]' --format moodle_progress,junit --out ,behatlog || true;<br />
kill $SELENIUM_PID<br />
kill $SELENIUM_PID<br />
kill $PHP_PID</code>
kill $PHP_PID</syntaxhighlight>
</div>
</div>


Line 185: Line 185:
<li>Add Publish JUnit test result report</li>
<li>Add Publish JUnit test result report</li>
<ul>
<ul>
<li>Change the pattern for <syntaxhighlight lang="php">Test report XMLs</code> to the value <syntaxhighlight lang="php">**/phpunit/phpunit.xml,**/behatlog/*.xml</code></li>
<li>Change the pattern for <syntaxhighlight lang="php">Test report XMLs</syntaxhighlight> to the value <syntaxhighlight lang="php">**/phpunit/phpunit.xml,**/behatlog/*.xml</syntaxhighlight></li>
</ul>
</ul>
</ul>
</ul>

Latest revision as of 13:06, 14 July 2021

This article describes how to use a Jenkins continuous integration server for automating PHPUnit and Behat tests for Moodle and Moodle plugins. The configuration uses Moodle's way of running PHPUnit and Behat tests using the command line.

Integration scenario: We are developing a Moodle plugin. We want to run automated tests, ensuring that it runs correctly on multiple Moodle platform versions, e.g., Moodle 2.7 and Moodle 2.8. This is motivated by the fact that we announce compatibility with these versions in the Moodle plugin directory, and we want to - at least - know, when a plugin becomes incompatible with an older version or needs to be changed. Since we are only running one recent Moodle version for development, automated tests with other versions are useful for us.

Prerequisites for this manual: Ubuntu-based integration server, headless (i.e. without a graphical environment), here: Ubuntu 14.04.

Installation of required software

  • Install Jenkins as a Ubuntu native package, as described on their website (instructions currently at http://pkg.jenkins-ci.org/debian/).
  • Install a Moodle runtime environment. A web server is not needed, since PHP 5's standalone server will be used. The following commands were used for our system:

sudo apt-get install postgresql<br />
sudo apt-get install php5-cli php-pear php5-curl php5-pgsql php5-gd php5-intl

  • Install git, which will be used to obtain the sources of Moodle and of your plugin.
sudo apt-get install git

  • For acceptance tests, you need to use a browser. Therefore you need to be able to run graphical applications. On headless servers (i.e. those without a display), you can install Xvfb, emulating a display which will be used by your browser.
sudo apt-get install xvfb firefox

  • Furthermore, acceptance tests require the Selenium standalone server to be installed. Download from http://www.seleniumhq.org/download/ (here: Version 2.45.0) and unpack it at a jenkins-accessible location, e.g. $JENKINS_HOME/selenium (here: /var/lib/jenkins/selenium).

Basic configuration of Jenkins using the shell

  • Configuration of the init script can be found at
    /etc/default/jenkins
    
    . It is not strictly necessary to change anything here, but we set
    HTTP_PORT=-1
    
    and added
    HTTPS_PORT=8998
    
    to make the server only via HTTPS. In that case, you also need to add
    --httpsPort=$HTTPS_PORT
    
    to the
    JENKINS_ARGS
    
    variable and restart Jenkins.
  • Furthermore, Moodle requires a
    config.php
    
    which is not contained in a repository. Therefore, configuration files need to be stored at a separate location, from where they can be pulled in. For our scenario, we stored the configuration file at
    $JENKINS_HOME/moodle-config/config_$target.php
    
    , where $target indicates a Moodle branch to build against (e.g., /var/lib/jenkins/moodle-config/config_MOODLE_28_STABLE.php).

Jenkins server configuration

For all following steps you need to open Jenkins in your browser.

Install or update the following plugins:

  • Xvfb plugin
  • Multiple SCMs plugin
  • Matrix project plugin
  • JUnit plugin
  • GitHub plugin
  • Job Configuration History Plugin (optional, but very useful)

Configure Jenkins in the following way (Manage Jenkins → Configure System):

  • Git
    • Add a git installation. Choose an arbitrary name and set the path to
      git
      
  • Git plugin
    • Set the fields
      Global Config user.name Value
      
      and
      Global Config user.email Value
      
      . In our case, we set those values to account and e-mail of a technical GitHub account that is only used by the Jenkins server

Create a job for your integration scenario:

  • On the dashboard, select New Item.
  • Choose Multi-configuration project and define an Item name without any spaces.
  • On the next page, define the following settings (Change to your requirements. Remember our integration scenario described above!):
    • GitHub project: URL to the web page of your repository
    • Source Code management:
      • Choose
        Multiple SCMs
        
        .
      • Use
        Add SCM
        
        to add two git repositories.
      • For the first repository,
        • set the URL to
          git://git.moodle.org/moodle.git
          
          .
        • Since this is a public repository, credentials are not required.
        • Under Additional Behaviours, add
          Advanced clone behaviours
          
          and check the option
          Shallow clone
          
          .
      • For the second repository,
        • set the URL to the URL of your git repository.
        • If this is a private repository, add credentials for accessing it (consider SSH authentication!)
        • Under Additional Behaviours, add
          Check out to a sub-directory
          
          and, for
          Local subdirectory for repo
          
          , enter the path where you would deploy your module in a usual Moodle installation (e.g.,
          mod/yourmodule
          
          ), without surrounding slashes.
        • Under Additional Behaviours, add
          Advanced clone behaviours
          
          and check the option
          Shallow clone
          
          .
    • Build Triggers:
      • Check Build when a change is pushed to GitHub.
      • Check Poll SCM. As a schedule, define e.g.
        H/5 * * * *
        
        for polling every five minutes.
    • Configuration Matrix:
      • Add two user-defined axes.
        • For the first axis, set name to
          target
          
          and values to
          MOODLE_28_STABLE MOODLE_27_STABLE
          
          . These values correspond to the branch names used in the Moodle repository.
        • For the second axis, set name to
          testtype
          
          and values to
          UnitTest BehatTest
          
          . These are used later on to perform different test types - be patient :).
      • Check Run each configuration sequentially.
    • Build Environment:
      • Check Start Xvfb before the build, and shut it down after.
    • Build:
      • Add Execute shell
        • Enter the following commands (Note that we use
          $target
          
          here, which always holds the current value of
          target
          
          in a particular configuration. Furthermore, this copies an appropriate config.php file into the current workspace):

git checkout $target;<br />
curl -s https://getcomposer.org/installer | php;<br />
php composer.phar install --prefer-source;<br />
cp $JENKINS_HOME/moodle-config/config_$target.php config.php;

  • Add Conditional step (single)
    • Run?:
      Strings match
      
      , String 1:
      ${testtype}
      
      , String 2:
      UnitTest
      
      (the motivation is that this command is only run for unit tests. In contrast, the other step will be for behat acceptance tests).
    • Builder:
      Execute shell
      
    • Enter the following commands:

mkdir -p ../moodledata/phpunit;<br />
php admin/tool/phpunit/cli/init.php;<br />
vendor/bin/phpunit --log-junit results/phpunit/phpunit.xml --group mod_ratingallocate || true;

  • Add Conditional step (single)
    • Run?:
      Strings match
      
      , String 1:
      ${testtype}
      
      , String 2:
      BehatTest
      
      .
    • Builder:
      Execute shell
      
    • Enter the following commands (replace
      [SeleniumVersion]
      
      ,
      [ModuleTag]
      
      , and
      [JobName]
      
      accordingly):

mkdir -p ../moodledata/behat;<br />
php admin/tool/behat/cli/init.php;<br />
java -jar /var/lib/jenkins/selenium/selenium-server-standalone-[SeleniumVersion].jar &gt; /dev/null 2&gt;&amp;1 &amp;<br />
SELENIUM_PID=$!<br />
php -S localhost:8000 &gt; /dev/null 2&gt;&amp;1 &amp;<br />
PHP_PID=$!<br />
vendor/bin/behat --config /var/lib/jenkins/jobs/[JobName]/workspace/target/moodledata/behat/behat/behat.yml --tags '@[ModuleTag]' --format moodle_progress,junit --out ,behatlog || true;<br />
kill $SELENIUM_PID<br />
kill $PHP_PID

  • Post-build Actions:
    • Add Publish JUnit test result report
      • Change the pattern for
        Test report XMLs
        
        to the value
        **/phpunit/phpunit.xml,**/behatlog/*.xml
        
  • Save the configuration and start the first build to test it.