<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kordan</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kordan"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Kordan"/>
	<updated>2026-08-19T05:18:32Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Conditional_activities&amp;diff=61462</id>
		<title>Conditional activities</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Conditional_activities&amp;diff=61462"/>
		<updated>2021-10-22T19:08:19Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Custom completion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}&lt;br /&gt;
= Design document =&lt;br /&gt;
(This is a design document by sam marshall from the Open University. It is based on earlier discussion in this wiki, and then detailed discussion with moodle.com staff who contributed improvements and corrections.)&lt;br /&gt;
&lt;br /&gt;
See MDL-15497 for tasks and tracking.&lt;br /&gt;
&lt;br /&gt;
sam marshall, 21 May 2008; updated as of 26 June 2008. Exported to MediaWiki format using OpenOffice.org 3.0 beta (i.e. this page doesn&#039;t perfectly represent the word-processor version) and manually tweaked a bit to fix it where it was obviously wrong.&lt;br /&gt;
== Introduction ==&lt;br /&gt;
This design document describes in some detail a proposed method to add support for ‘conditional activities’ to Moodle. It is based on a [https://docs.moodle.org/en/Development:Conditional_activities discussion on the Moodle wiki], conversations with Moodle.com staff, and only the teeniest pinch of OU requirements. &lt;br /&gt;
&lt;br /&gt;
This is divided into two related, but separate, chunks of work.&lt;br /&gt;
=== Completion tracking ===&lt;br /&gt;
Make the system store whether or not an activity has been ‘completed’. &lt;br /&gt;
&lt;br /&gt;
This may be according to an activity-specific definition of completion, or because the student has ticked a box saying they’ve finished it.&lt;br /&gt;
&lt;br /&gt;
When tracked automatically, completion could vary from formal requirements such as ‘scoring 90% on this quiz’ to informal requirements intended to ensure participation such as ‘post at least 3 times on this forum’.&lt;br /&gt;
&lt;br /&gt;
It should be possible for teachers to access progress information for their students, so that they can see if anyone is falling behind.&lt;br /&gt;
=== Conditional availability ===&lt;br /&gt;
Allow or prevent access to an activity depending on various conditions.&lt;br /&gt;
&lt;br /&gt;
These conditions may include the completion of other activities; in the case of graded activities, also whether the grade was pass/fail, or a specific amount. It also may include date.&lt;br /&gt;
=== Development process ===&lt;br /&gt;
I will do this work here. It is possible that time constraints might mean I only do the first part (completion tracking).&lt;br /&gt;
&lt;br /&gt;
Development will be against Moodle 1.9, because we need it here at the OU, but it will never actually be committed into Moodle 1.9. The code will instead be ported to Moodle 2.0, which will require various changes (particularly in the area of database access).&lt;br /&gt;
&lt;br /&gt;
Code will be PHP5. We will primarily test against Postgres here but intend using standard Moodle database API and only simple SQL where needed, so database compatibility is unlikely to be a problem.&lt;br /&gt;
&lt;br /&gt;
In some cases the OU have versions of these features. I will be coding automatic upgrade from the previous versions. This will not be included in the core Moodle 2.0 version.&lt;br /&gt;
== Completion tracking ==&lt;br /&gt;
=== Completion options ===&lt;br /&gt;
The completion system can be enabled or disabled at site, course, and activity level.&lt;br /&gt;
* At site level, the new admin variable $CFG-&amp;gt;enablecompletion (defaults to 1?).&lt;br /&gt;
* At course level, a new mdl_course field enablecompletion (defaults to 1).&lt;br /&gt;
* At activity level, a new mdl_course_modules field completion with values 0 = none, 1 = manual (student decides when they’ve completed something), 2 = automatic. Defaults to 1 but may be set by module e.g. a quiz module could default to automatic, label could set it to 0 so you don’t get checkboxes beside each label, etc.&lt;br /&gt;
This table illustrates the effect of these options. (‘enablecompletion’ is abbreviated to ‘ec’.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;prettytable&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Location&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Feature&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Appears if&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Admin settings&lt;br /&gt;
| On-off toggle for $CFG-&amp;gt;ec&lt;br /&gt;
| –&lt;br /&gt;
|-&lt;br /&gt;
| Course settings&lt;br /&gt;
| On-off toggle for course ec&lt;br /&gt;
| $CFG-&amp;gt;ec&lt;br /&gt;
|-&lt;br /&gt;
| Activity settings&lt;br /&gt;
| Off/manual/automatic option for course-module completion&lt;br /&gt;
| $CFG-&amp;gt;ec &amp;amp;&amp;amp; course ec&lt;br /&gt;
|-&lt;br /&gt;
| Course view&lt;br /&gt;
| Completion display/controls against activity&lt;br /&gt;
| $CFG-&amp;gt;ec &amp;amp;&amp;amp; course ec &amp;amp;&amp;amp; completion&lt;br /&gt;
|-&lt;br /&gt;
| Admin block&lt;br /&gt;
| Link to progress report&lt;br /&gt;
| $CFG-&amp;gt;ec &amp;amp;&amp;amp; course ec &amp;amp;&amp;amp; user has permission&lt;br /&gt;
|}&lt;br /&gt;
=== Completion states and conditions ===&lt;br /&gt;
Completion state is stored per-user for every activity. There are four states:&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;Not completed [default – not stored in database]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Completed (pass/fail unspecified)&lt;br /&gt;
* Completed, passed&lt;br /&gt;
* Completed, failed&lt;br /&gt;
The final two states only apply when completion is based on a specific grade result. These are used only in some situations. Here are the rules: &lt;br /&gt;
* Grade visible, ‘grade to pass’ set: ‘completed, passed’ and ‘completed, failed’ states will be used.&lt;br /&gt;
* ‘Grade to pass’ not set &#039;&#039;or&#039;&#039;grade is hidden &#039;&#039;or&#039;&#039;grade is hidden-until (regardless of until date): only the standard ‘not completed’, ‘completed’ states are used.&lt;br /&gt;
This is a ‘push’ system – the state is not computed live but is saved and that value used. When completion for the activity is set to manual, students can toggle this value between not completed and completed. When it is set to automatic, the system adjusts the value in certain cases.&lt;br /&gt;
==== Standard conditions ====&lt;br /&gt;
There are two standard conditions which do not require (much) module-specific behaviour.&lt;br /&gt;
* Graded – module has assigned grade for student.&lt;br /&gt;
** If there are multiple grades for this activity, you can select which one is used to determine completion. (We will not implement this selection in the user interface for the first release. It will always use the first grade.)&lt;br /&gt;
** This condition is implemented by a hook in gradebook that runs at the point where a grade is set.&lt;br /&gt;
* Viewed – student has viewed module.&lt;br /&gt;
** For this to be supported, the module must call a function completion_set_module_viewed($cm) when it thinks that the user has ‘viewed’ the activity. It also needs to indicate that it supports it via &#039;&#039;module&#039;&#039;_supports(FEATURE_COMPLETION_TRACKS_VIEWS).&lt;br /&gt;
==== Custom conditions ====&lt;br /&gt;
Modules can support arbitrary custom conditions (with their own form components to configure these, and their own data storage to track them).&lt;br /&gt;
&lt;br /&gt;
Example conditions might be:&lt;br /&gt;
* Forum&lt;br /&gt;
** User has made N posts.&lt;br /&gt;
** User has made N replies.&lt;br /&gt;
** Other people have posted N replies to discussions created by this user. (&#039;&#039;This an example of a more complex possibility, not necessarily something that would be a good idea.&#039;&#039;)&lt;br /&gt;
* Wiki&lt;br /&gt;
** User has edited N different pages.&lt;br /&gt;
** User has edited pages N times.&lt;br /&gt;
* Choice&lt;br /&gt;
** User has voted.&lt;br /&gt;
It is up to the module to determine when a condition has been met and update the user’s completion. &lt;br /&gt;
&lt;br /&gt;
As part of this project I plan to create some custom conditions for one or a few modules (probably just forum), and implement view tracking for a wider range of modules.&lt;br /&gt;
==== Combining conditions ====&lt;br /&gt;
If an activity has both the standard grade condition, and module conditions, these are currently combined via Boolean AND. Modules that support multiple conditions should usually also AND them as this will make life simpler for developer and users; however this is up to the module, as it creates its own form fields and data items.&lt;br /&gt;
&lt;br /&gt;
In the future we may (or may not!) provide the ability to choose boolean OR instead. Some of the API functions contain a parameter that indicates how results should be combined. At present this is always set to AND.&lt;br /&gt;
=== Expected date ===&lt;br /&gt;
An activity can optionally have an expected completion date. This date currently has no effect and is not shown to students, but it appears to teachers when viewing progress. It&#039;s intended so that teachers can see when students are having problems and maybe offer assistance.&lt;br /&gt;
&lt;br /&gt;
In future it might be possible to export this data – e.g. the list of students who are &#039;late&#039; – to external systemss.&lt;br /&gt;
=== Database ===&lt;br /&gt;
(&#039;&#039;Prefix is shown as _. Constants are defined for all values here.&#039;&#039;)&lt;br /&gt;
* _course&lt;br /&gt;
** +Field enablecompletion, unsigned int (1) default 0 not null.1 = enable completion options for activities (if enabled at site level). This is the user-interface default that applies to new courses when completion is enabled for the site.0 = disable and hide options. This is the database default that will apply to existing courses.&lt;br /&gt;
* _course_modules&lt;br /&gt;
** +Field completion, unsigned int (1) default 0 not null.0 = No progress tracking for this activity.1 = Manual completion tracking.2 = Automatic completion tracking.&lt;br /&gt;
** +Field completiongradeitemnumber, unsigned int(10) default 0.NULL = Grade information not used for completion.0,1,… = Use first/second/N&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt; grade item supplied by this activity.&lt;br /&gt;
** +Field completionview, unsigned int (1) default 0 not null.0 = View not required for completion.1 = View required for completion.&lt;br /&gt;
** +Field completionexpected, unsigned int (10) default 0 not null.0 = No particular date expected for completion&amp;lt;nowiki&amp;gt;* = time (seconds since epoch) by which activity is expected to be completed. &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* New table _course_modules_completion which stores completion state for a user on each activity.&lt;br /&gt;
** id.&lt;br /&gt;
** coursemoduleid&amp;lt;nowiki&amp;gt; [index].&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
** userid&amp;lt;nowiki&amp;gt; [index]&amp;lt;/nowiki&amp;gt;.&lt;br /&gt;
** completionstate, unsigned int(1) not null.(The absence of a row in the table counts the same as 0 here.)0 = not completed.1 = completed.2 = completed, passed.3 = completed, failed.&lt;br /&gt;
** viewed, unsigned int(1)null = not tracked0 = not viewed1 = viewedOnly stored if the activity is tracking viewed state.&lt;br /&gt;
** timemodified, unsigned int (10) not null.Last date the state changed, in seconds since epoch.&lt;br /&gt;
=== Capabilities ===&lt;br /&gt;
* moodle/course:viewprogress&amp;lt;nowiki&amp;gt; [default Teacher, Editing Teacher, Course Creator]&amp;lt;/nowiki&amp;gt;Users with this permission can view progress of students on the course. If the course is set to visible groups mode, or the user also has accessallgroups, then they can see all groups. Otherwise they can only see groups they belong to.&lt;br /&gt;
=== Admin ===&lt;br /&gt;
A new admin setting progresstrackedroles stores a list of the roles that are considered relevant when displaying progress. (Other roles can still use the completion system, but won’t be shown on the progress screen.) This setting is editable via standard admin screens.&lt;br /&gt;
=== API ===&lt;br /&gt;
These functions will be included in a completionlib.php.&lt;br /&gt;
* completion_is_enabled($course=null,$cm=null)Returns true if completion is enabled. With no parameters, returns the site value; when course is specified, checks course as well.If course-module is specified, the return value is the same as the value of completion from course_modules.&lt;br /&gt;
* completion_update_state($course,$cm, $possibleresult=COMPLETION_UNKNOWN,$userid=0)Called to update completion state on the activity $cm for the given or current user. This obtains grade/viewable information if necessary and then asks the module whether the activity has been completed.It is called whenever a grade is updated on an activity that uses grades for completion and, by modules, whenever a module-specific piece of data that might affect completion has changed. &lt;br /&gt;
* $possibleresult is included for performance reasons to avoid recalculating completion when not necessary. Set to COMPLETION_COMPLETE or COMPLETION_NOTCOMPLETE, it indicates that the change which necessitated calling this function can only result in that type of state. For example, if there is a condition that the user must make 5 forum posts, we would call this function in response to any new post. But if the state is already ‘complete’, there is no point going through counting posts, checking grades, etc. This hint allows the code to skip further work if the user’s state is already the target. Once retrieved we will cache the state in session so that frequently, no database queries are required.&lt;br /&gt;
* For performance reasons, modules should still only call this function when necessary. The forum should not call this function whenever a new post is made, unless the completion option for counting posts is actually turned on for that forum.&lt;br /&gt;
* When a module is set to manual completion, this function is used to directly toggle completion. The completion value is set to $possibleresult.&lt;br /&gt;
* completion_set_module_viewed($course,$cm,$userid=0)Called when a module is viewed. If view-based completion is enabled (in $cm) then it sets the &#039;viewed&#039; flag and calls update_completion_state.&lt;br /&gt;
* completion_count_user_data($cm)Called to determine whether any users have already completed an activity (if so, returns the number). This is used by the module settings form to determine whether completion settings should be &#039;locked&#039;.&lt;br /&gt;
* completion_delete_all_state($course,$cm)Called at the point where course-module entries are deleted. Deletes all completion state data for that course-module.&lt;br /&gt;
* completion_reset_all_state($course,$cm)Deletes existing completion state for all users in this activity, then recalculates it by calling update_completion_state a lot for (i) all state-tracked users (those with progresstrackedroles roles in this activity’s context), (ii) all users who have stored completion state for this activity.Needs to be called if completion conditions change.&lt;br /&gt;
* completion_get_data($course,$cm,$wholecourse=false,$userid=0)Obtains completion data for a particular user related to a given activity. User defaults to current user.The $wholecourse parameter is a hint that, if it isn&#039;t already available, the system should retrieve into cache the completion data for all course activities now (so that future requests for other activities can be satisfied from cache).&lt;br /&gt;
* completion_get_activities($course)Gets course-module objects for all activities on a course that have completion turned on. (&#039;&#039;Used by the progress report.&#039;&#039;)&lt;br /&gt;
* completion_get_progress_all($course,$sortfirstname=false,$groupid=0)Obtains progress of all users in a course (or group) across all activities in that course for which completion tracking is enabled. (&#039;&#039;Used by the progress report.&#039;&#039;)&lt;br /&gt;
* completion_inform_grade_changed($course,$cm,&amp;amp;$item,&amp;amp;$grade,$deleted)Called to notify the completion system when a user&#039;s grade changes. Does nothing (quickly) if completion is not enabled for the item; if completion is enabled and depends on grade, calls completion_update_state. (&#039;&#039;Provided only for use by the gradebook.&#039;&#039;)&lt;br /&gt;
=== Module changes ===&lt;br /&gt;
There is no requirement for any module changes to support the completion system. Existing modules continue to work.&lt;br /&gt;
&lt;br /&gt;
If no changes are made to a module, then manual completion (where the user ticks the box themselves) is supported for activities of that type, and automatic completion is not offered as an option in the activity settings screen.&lt;br /&gt;
&lt;br /&gt;
Progressively more changes are required when implementing more detailed completion support for a module.&lt;br /&gt;
==== View support ====&lt;br /&gt;
To support the &#039;complete when viewed&#039; feature, a module must do the following:&lt;br /&gt;
* Implement &#039;&#039;module&#039;&#039;_supports() as follows:function &#039;&#039;module&#039;&#039;&amp;lt;nowiki&amp;gt;_supports($feature) { switch($feature) { case FEATURE_COMPLETION_TRACKS_VIEWS: return true; default: return false; }}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Add calls to completion_set_module_viewed whenever the module considers that it has been &#039;viewed&#039;.&lt;br /&gt;
** It is up to the module to determine what this means. However most modules might put this call in view.php, near print_footer once it is clear that no errors occurred.&lt;br /&gt;
** This function does not take a significant time to run if view-based completion is not enabled, so you don&#039;t need to put the call inside an if.&lt;br /&gt;
==== Grade support ====&lt;br /&gt;
If a module provides grades, making &#039;&#039;module&#039;&#039;_supports() return true for FEATURE_GRADE_HAS_GRADES will make these accessible to the completion system.&lt;br /&gt;
==== Custom completion ====&lt;br /&gt;
Supporting module-specific custom completion conditions requires extra work:&lt;br /&gt;
* Decide how to store the settings for these extra conditions (probably in your module&#039;s main table). Change your database tables to allow this.&lt;br /&gt;
* Add FEATURE_COMPLETION_HAS_RULES to your &#039;&#039;module&#039;&#039;_supports().&lt;br /&gt;
* Add the extra controls to your mod_form.php.&lt;br /&gt;
** Override the method add_completion_rules() so that it adds extra form elements for your controls (the method is called at the right point so that they are added in the &#039;completion&#039; box), and returns a list of the form elements (this is used to enable/disable them appropriately).&lt;br /&gt;
** Override the function completion_rule_enabled($data) so that it returns true if the supplied data means that at least one of the module&#039;s completion conditions is turned on. This is used to prevent the user from choosing &#039;automatic&#039; completion if they don&#039;t also enable at least one condition.&lt;br /&gt;
** You may need to modify other functions such as definition_after_data() and get_data to set up your form controls appropriately based on the data from the database, or vice versa.&lt;br /&gt;
* In your lib.php, implement the new function &#039;&#039;module&#039;&#039;_get_completion_state($course,$cm,$userid,$logic).&lt;br /&gt;
** If the specified module instance has completion requirements, this should return true (if they have been met) or false (if they haven&#039;t). The system will later combine this value with possible other completion requirements (view or grades).&lt;br /&gt;
** $logic is either COMPLETION_AND or COMPLETION_OR, indicating which type of Boolean logic should be used when there are multiple conditions. &lt;br /&gt;
** If completion conditions are not enabled for this particular instance, the function should return $logic (not false). &lt;br /&gt;
* In your module code, when something changes which might affect completion, call completion_update_state&lt;br /&gt;
=== Example of system operation ===&lt;br /&gt;
Assume that the forum offers an option to check completion based on the number of posts (complete when you’ve made N posts).&lt;br /&gt;
&lt;br /&gt;
Every time a user makes a new post:&lt;br /&gt;
* Forum checks if completion tracking based on the number of posts is enabled (this would probably be an option in $forum). If not, it does nothing (&#039;&#039;stop&#039;&#039;).&lt;br /&gt;
* Forum calls completion_update_state($cm,COMPLETION_COMPLETE).&lt;br /&gt;
* System looks up the user’s current completion state for $cm. If the current state is already COMPLETION_COMPLETE (or COMPLETE_PASS or COMPLETE_FAIL) then it returns with no further action (&#039;&#039;stop&#039;&#039;).&lt;br /&gt;
* If grade-based completion is enabled, system obtains grade and checks it. If viewed-completion is enabled, system checks whether the module has been viewed.&lt;br /&gt;
* If the forum has its own completion rules, then the system calls forum_get_completion_state().&lt;br /&gt;
* The forum checks its own rules, including the number of posts and anything else that’s enabled. It returns true if these rules are met and false if they’re not.&lt;br /&gt;
* Combining this data, the system decides whether to update the completion state. If an update is needed, it makes the change in the database.&lt;br /&gt;
=== Activity UI (course page) ===&lt;br /&gt;
The user interface for students on the course view page needs to allow for displaying progress to students and, where completion is set to manual, allowing students to tick their own progress boxes. &lt;br /&gt;
&lt;br /&gt;
I have based this suggested interface on experience here at the OU. The interface will be implemented via changes to print_section and course view.php.&lt;br /&gt;
* Completion displays alongside each activity for which completion is enabled, to the right of the section. (This conveniently separates it from the functional information at the left, and means that all the progress markers line up with each other.)&lt;br /&gt;
* Automatic completion displays as a tick or cross icon, by default coloured green and red (accessible colours will be chosen), or some kind of faint outline if something isn’t completed yet. The cross icon is used if completion state is 1.&lt;br /&gt;
* Manual completion displays using two more icons (‘manual tick’ and ‘manual blank’). Possible icons would be the same tick with a heavy dotted box around it, and an empty dotted box. The title of the icon is an interface clue to the user e.g. ‘Click to mark this activity completed’.When AJAX is not enabled, the manual icons link to a URL that toggles the completion value then redirects straight back to the course page.When AJAX is enabled, the manual icons use YUI to adjust the completion value without reloading the page. In this case some kind of animation – for example, the word ‘Saved’ appearing next to the icon for a second before fading away – will be used to indicate to users that their change has taken effect.&lt;br /&gt;
* If completion is not enabled for an activity (or for the course, site, etc) then the print_section display is the same as at present.&lt;br /&gt;
* When editing is turned on, completion information always displays as ticked regardless of the user’s actual completion setting. This makes it easy to see that completion is enabled for that activity.&lt;br /&gt;
I will get a graphic designer to look at the icons to use. To reiterate, there are six icons:&lt;br /&gt;
* automatic completion, not completed yet (above, not actually shown on its own, but would be a faint grey dotted box)&lt;br /&gt;
* automatic completion, completed (above, slightly grey tick, with faint grey dotted surround)&lt;br /&gt;
* automatic completion, pass (not shown, e.g. could be a green tick)&lt;br /&gt;
* automatic completion, fail (not shown, e.g. could be a red cross)&lt;br /&gt;
* manual completion, not completed (heavy black dotted outline)&lt;br /&gt;
* manual completion, completed (heavy black dotted outline with black tick)&lt;br /&gt;
It may need some work to make the manual ones look clickable and the automatic ones look not clickable, but not irrelevant either. For example we might have to resort to crappy 3D button-style shading on the manual ones. This is what I&#039;ve done (badly) in the initial version of the icons which I have made.&lt;br /&gt;
=== Configuration UI (activity settings page) ===&lt;br /&gt;
Each activity’s completion settings can be configured on the standard activity settings page. These settings only appear if completion is enabled for the course and site.&lt;br /&gt;
&lt;br /&gt;
The interface will be implemented via a change to the standard_coursemodule_elements function and a new optional form member function completion_add_rules(), as mentioned above, which is called (from within standard_coursemodule_elements) to add the activity-specific form elements.&lt;br /&gt;
&lt;br /&gt;
We also need to determine whether the activity supports grades. This will be determined via the new &#039;&#039;module_&#039;&#039;supports() function. (Note that there are existing functions for finding out whether a module &#039;&#039;instance&#039;&#039; has a grade, but not for finding out whether a module supports grades if it hasn&#039;t been created yet.)&lt;br /&gt;
&lt;br /&gt;
((TODO: Diagram needs redoing))&lt;br /&gt;
* A dropdown allows choice of the three completion options – none, manual, automatic. (All three are shown in the diagrams below.) If the module does not support its own completion conditions, and does not support grades, then the ‘automatic’ option will not appear.&lt;br /&gt;
* A ‘view’ checkbox only appears if the module supports view tracking. If completion is not set to automatic, it will be greyed out. It allows you to mark something complete as soon as the user goes there once.&lt;br /&gt;
* A &#039;require grade&#039; checkbox only appears if the module supports grades. If completion is not set to automatic, it will be greyed out.&lt;br /&gt;
** At this point we could have a dropdown selector to choose which grade item to use if there is more than one. However, at least for the first release, I am not providing a user interface for that feature.&lt;br /&gt;
* Module-specific settings, if any, are included. These are also greyed out if completion is not set to automatic. The module can add any number of settings. It is responsible for storing the data from these as part of its normal form processing.&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;An expected completion date (default disabled) is given. This can be enabled for manual or automatic completion. It has no practical effect but displays on the progress screen. The intention is that those who design a course can set these dates at key points in the course. During the course, teachers can look at the progress report and use the dates as a guideline to check whether students are falling behind. [At a later date it might be possible to do other things with this data, such as export a list of &#039;late&#039; students to an external system.]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* If anybody already has stored completion state for this activity, completion settings will be locked: they appear, but read-only. A button allows you to unlock the settings (making those fields editable again); after you do that and save the form, existing completion data is wiped and recalculated against the new conditions. A warning by the button explains that changing completion conditions after students complete things is not a good idea.&lt;br /&gt;
=== Progress tracking UI ===&lt;br /&gt;
Teachers wishing to track student progress can do so via the admin block. The existing course reports page will include a link to view student progress if the user has viewprogress capability.&lt;br /&gt;
&lt;br /&gt;
The resulting page shows a summary of progress either for a particular group, or for all students in the course, according to the normal rules for group selection. It may include a standard group dropdown too.&lt;br /&gt;
# Progress is shown in a table. Students (everyone with a role in progresstrackedroles&amp;lt;nowiki&amp;gt;) are displayed down the left of the table and for activities that have completion enabled, the activity names [with expected completion dates, if set] are displayed across the top. Students can be sorted by first name or last name. The student names link to profiles and activity names link to the activities.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
# The cells in this grid show progress indicators using the same icons as used on the course view page, although these icons are not clickable in the report. For each cell, the title, which you can see by hovering over it, indicates the state of the cell and the time modified (‘Completed 12/9/08 13:47’) as well as the student&#039;s name and the activity name (in case you are scrolling around a table that doesn&#039;t fit on-screen).&lt;br /&gt;
# A link at the bottom allows this table to be downloaded in CSV format. The downloaded version contains the same data, using two columns (‘state’ and ‘date’) for each activity.&lt;br /&gt;
=== Performance ===&lt;br /&gt;
==== Course view ====&lt;br /&gt;
When completion is enabled for a course, the course view page needs to obtain completion information. This will be cached in session and retrieved efficiently (hopefully 1 query) the first time.&lt;br /&gt;
==== Module pages ====&lt;br /&gt;
There is no performance impact when using modules that do not support completion, or where completion is disabled.&lt;br /&gt;
&lt;br /&gt;
When you take an action that potentially involves completion (e.g. if you make a forum post to a forum that has enabled automatic completion when you reach N posts) the same completion state cache is used to read the current state of the data, so this should need no queries (you probably already saw the front page). &lt;br /&gt;
&lt;br /&gt;
If your action could potentially change that completion state (e.g. you have not already completed the activity and you just made an additional forum post) there will be an additional cost at that time, depending on the activity – in this case, the forum will do a query to find out how many posts you&#039;ve made. Because these actions occur relatively rarely, this should not cause a serious problem. (Once you have completed the activity, making another post will not incur the performance penalty.) &lt;br /&gt;
&lt;br /&gt;
View-related completion tracking, the part most potentially problematic, will behave similarly: the first time you view something that has a &#039;viewed&#039; requirement, it will make extra queries as it marks it viewed (or, if you viewed it in a previous session, loads up that information). After that the information will be cached in your session.&lt;br /&gt;
=== Backup and restore ===&lt;br /&gt;
* When users are included in backup, course backup will include the contents of the _course_modules_completion table.&lt;br /&gt;
* Course and course-modules backup will be modified to include the new fields in those tables.&lt;br /&gt;
== Conditional availability ==&lt;br /&gt;
=== Conditional options ===&lt;br /&gt;
There is no specific option to enable this part of the feature (?). The parts which depend on the completion system are enabled using the completion flag.&lt;br /&gt;
=== Available conditions ===&lt;br /&gt;
You can place conditions on any activity. Conditions are always combined with Boolean AND, i.e. all conditions must be met before the activity becomes available. Unlike completion, conditions are not module-dependent. There are two types of condition:&lt;br /&gt;
# Conditional on date. You can have both of these conditions if required.&lt;br /&gt;
#* On or after date – activity becomes available on given date.&lt;br /&gt;
#* Before date – activity becomes unavailable on given date.&lt;br /&gt;
# Conditional on other activities. You can have as many of these conditions as desired.&lt;br /&gt;
#* Activity must be completed – any kind of completion will do.&lt;br /&gt;
#* Activity must be completed and passed – completion must have been graded and displayed with a pass mark (tick icon).&lt;br /&gt;
#* Activity must be graded at least/at most &#039;&#039;N&#039;&#039;.&lt;br /&gt;
=== Database ===&lt;br /&gt;
* _course_modules&lt;br /&gt;
** New field availablefrom, unsigned int (10) not null default 0.0 = Not conditional on date.&amp;lt;nowiki&amp;gt;* = Time (seconds since epoch&amp;lt;/nowiki&amp;gt;) after which activity becomes available.&lt;br /&gt;
** New field availableuntil, unsigned int(10) not null default 0.0 = Not conditional on date.&amp;lt;nowiki&amp;gt;* = Time (seconds since epoch) after which activity becomes unavailable.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
** New field showavailability, unsigned int (1) not null default 0.0 = Activity does not appear at all if conditions are not met.1 = If conditions are not met, activity appears but greyed out (‘hidden’ style) with no link and a short explanation of when it will be available.&lt;br /&gt;
* New table _course_modules_avail(able?) which stores any per-module conditions.&lt;br /&gt;
** id.&lt;br /&gt;
** coursemoduleid&amp;lt;nowiki&amp;gt; [index]&amp;lt;/nowiki&amp;gt;.ID of the module that is being restricted.&lt;br /&gt;
** cmsourceid.ID of module that the restriction is based on.&lt;br /&gt;
** requiredcompletion, unsigned int(1) not null default 0.0 = no required completion.1 = required completed.2 = required completed and passed.3 = required completed and failed.&lt;br /&gt;
** gradesign, varchar(2).NULL = no required grade.&amp;lt;nowiki&amp;gt;‘&amp;lt;’, ‘&amp;lt;=&amp;lt;/nowiki&amp;gt;’, ‘&amp;gt;’, ‘&amp;gt;=’ = requirement for grade.&lt;br /&gt;
** requiredgrade, number(10,5 decimals).Grade boundary used together with the above sign. May be null.&lt;br /&gt;
=== API ===&lt;br /&gt;
Two new functions:&lt;br /&gt;
* availability_is_available($cm)Returns true if the module is available to students (it is visible, plus availability conditions, if any, are met). Does not include course access conditions, which it assumes have already been checked.This is a slot-in replacement where code checks for $cm-&amp;gt;visible at key points such as in require_login.&lt;br /&gt;
* availability_get_display_details($cm)Obtains information about the activity’s availability which is required in order to display (or not display) a link to the activity. Returns an object with the following fields:-&amp;gt;available – If true, activity is available; users should be allowed to access it / get links to it.-&amp;gt;show – If true, activity should be displayed (even to users who do not have viewhiddenactivities)-&amp;gt;info – Informational string. Blank if the activity is available.&lt;br /&gt;
Other changes:&lt;br /&gt;
* For performance reasons we need to cache the conditions in _course_modules_available. Best option is to use existing get_fast_modinfo infrastructure. This should not be hard. The above two functions will use this cache.&lt;br /&gt;
=== Configuration UI (activity settings page) ===&lt;br /&gt;
The configuration UI will be implemented as part of standard_coursemodule_elements in the module forms. The existing ‘visible to students’ option (not changed in database/code) is moved within this UI. &lt;br /&gt;
&lt;br /&gt;
This UI will appear even if completion is not enabled, but the parts related to completion will not show. So if completion is not enabled you will still be able to set available dates if you like, and the interface will remain consistent. The diagram below shows, first, the version that appears with completion not enabled, and then the one you get with completion enabled.&lt;br /&gt;
&lt;br /&gt;
((There should be a diagram here, not sure it will show in the wiki version))&lt;br /&gt;
&lt;br /&gt;
[[Image:]]&lt;br /&gt;
* The actual ‘available from’ options should include time as well as date. ‘Available from’ should default to midnight tomorrow; ‘Available until’ should default to 23:59 today. (I got bored laying out combo boxes.)&lt;br /&gt;
* ‘Information display’ controls whether informational text is displayed to students when an activity is not available. (See below.) The default is not to show information.&lt;br /&gt;
* The list of other activities which might have conditions includes only those other activities on the course which either have completion information, or a grade. Anything which doesn’t have completion information or a grade is omitted.&lt;br /&gt;
* Only one condition is permitted per other activity. This isn’t a restriction of the back-end, it just makes the UI simpler.&lt;br /&gt;
* The condition type dropdown by each activity is adjusted dynamically to include only options that are valid for that activity:&lt;br /&gt;
** ‘Completed’ appears only if completion is enabled for the activity.&lt;br /&gt;
** ‘Completed with pass’ and ‘Completed with fail’ (which I missed off the diagram by accident) appear only if completion is enabled for the activity, and set to a grade item, and the grade item contains a pass mark.&lt;br /&gt;
** The grade conditions appear only if the activity has a grade item.&lt;br /&gt;
* The grade box is a text-entry field. Users type in the floating-point number version of the required grade.&lt;br /&gt;
=== Activity UI (course page) ===&lt;br /&gt;
(This will be accomplished via code changes to print_section. Logic is basically the same as for the current visible flag.) &lt;br /&gt;
&lt;br /&gt;
For available activities, the display obviously does not change. Unavailable activities will be displayed as follows:&lt;br /&gt;
* If the current user has viewhiddenactivities:&lt;br /&gt;
** The activity displays using the CSS style for hidden activities.&lt;br /&gt;
** The informational message as to why the activity isn’t available is displayed (even if showinfo is not set).&lt;br /&gt;
* If the current user does not have viewhiddenactivities:&lt;br /&gt;
** If showavailability is not set, or if the activity is actually hidden (visibility 0), the activity does not display at all.&lt;br /&gt;
** If showavailability is set and the activity isn’t hidden but is unavailable for some other reason, the name of the activity displays using the CSS style for hidden activities, but does not include an actual link to the activity. The informational message as to why the activity isn’t available is displayed.&lt;br /&gt;
Here are some examples of hidden activities with informational text:Assignment resources Available from 6 Oct 2008Assignment resources Available when Unit 1 Test grade is &amp;gt; 90Assignment resources Available when Tutor group forum is marked complete (+ other requirements)When there are multiple conditions, only one is shown. Date is considered first; the order after that is arbitrary.&lt;br /&gt;
&lt;br /&gt;
Behaviour is the same when editing is turned on except that the ‘availability’ string moves to the line below so as not to clash with all those icons.&lt;br /&gt;
=== Performance ===&lt;br /&gt;
This adds a single quick database query to the course view page for courses which have enabled conditional activities / completion.&lt;br /&gt;
&lt;br /&gt;
All other information is cached in modinfo&amp;lt;nowiki&amp;gt;, so does not have a performance penalty, save one problematic issue: the feature that allows arbitrary grade information to be considered. If you use pass/fail information, that’s fine, it’s there already in the completion data. But if you use a specific grade [and for example it could be different values for different activities] then the actual grade for the item needs to be retrieved on course view.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I don’t believe this will pose a massive performance issue as it only applies when modules are used as source for this kind of information. We should review this once it’s developed and, if necessary, optimise performance then. There are various caching approaches we could use.&lt;br /&gt;
=== Module delete ===&lt;br /&gt;
If you try to delete a module, it will check to see if that module is referenced by any other module as a condition. If that’s the case, you will not be allowed to delete it. An error message will include a link to the module edit page for the other module (so you can go there and delete the condition if you like).&lt;br /&gt;
=== Backup and restore ===&lt;br /&gt;
* The new _course_modules_available table will be backed up and restored.&lt;br /&gt;
** Partial restore needs to be handled. If your backup includes module A but not module B, and module A had a condition on B, then that condition will not be restored.&lt;br /&gt;
* Backup will also be modified to include the fields added to _course_modules.&lt;br /&gt;
* If you restore a course with a startdateoffset, it will offset the dates from the new _course_modules date fields.&lt;br /&gt;
== Module supported features ==&lt;br /&gt;
As part of the completion system, I needed a way for modules to indicate support of certain features, so I propose a generic one which I have implemented:&lt;br /&gt;
* A module API function &#039;&#039;module&#039;&#039;_supports($feature) which returns false if the feature is not available, and true (or an object with more information, if needed for specific features) if it is.&lt;br /&gt;
* A standard API function module_supports($modulename,$feature) which works as follows:&lt;br /&gt;
** If the $modulename_supports_feature function exists, it calls that function and returns the result.&lt;br /&gt;
** Otherwise it returns false – or, if features are added that correspond to ‘legacy’ ones that we can find out another way (e.g. by looking to see if a function exists) then this can be done.&lt;br /&gt;
* A set of FEATURE_&#039;&#039;xx&#039;&#039; constants. For this code, the following are necessary:&lt;br /&gt;
** FEATURE_GRADE_HAS_GRADE&lt;br /&gt;
** FEATURE_COMPLETION_TRACKS_VIEWS&lt;br /&gt;
** FEATURE_COMPLETION_HAS_RULES&lt;br /&gt;
* Other feature constants could be added as desired, e.g. FEATURE_CRON, etc. My initial implementation will include only the &#039;new&#039; ones needed for this work.&lt;br /&gt;
Note that this implementation would not be appropriate if modules were changed to be fully object-oriented. However I don&#039;t expect that to happen in Moodle 2 so this is probably OK.&lt;br /&gt;
==See also==&lt;br /&gt;
* [[Conditional activities_Adding module support]]&lt;br /&gt;
* [[Conditional activities]]&lt;br /&gt;
[[Category:Conditional activities]]&lt;br /&gt;
[[Category:Completion]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Survey_2_module&amp;diff=49058</id>
		<title>Survey 2 module</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Survey_2_module&amp;diff=49058"/>
		<updated>2015-11-20T12:27:39Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = New Survey module&lt;br /&gt;
|state = Development in progress&lt;br /&gt;
|tracker = MDL-36350&lt;br /&gt;
|discussion = https://moodle.org/mod/forum/discuss.php?d=215159&lt;br /&gt;
|assignee = MediaTouch 2000 srl - Daniele Cordella&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;I am going to add make unstable surveypro since next monday with further developments.&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Because of this, anybody wanting to upgrade the surveypro module up to current stable status should be use MOODLE_30_STABLE branch, no matter the Moodle version used (from 2.6.x to 3.0.x).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Since Monday November 23, 2015, master will be for development, and only will work under Moodle 3.0.0 and newer. MOODLE_30_STABLE will be the stable supported branch were fixes will be applied.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.6}}This page is for collecting feature requests for a the new Survey module that replaces Survey, Questionnaire and Feedback.&lt;br /&gt;
&lt;br /&gt;
The module is currently under development, so the general features list will also include a completion status, if any. For those who like to test a prototype of the module a Github repo is available [https://docs.moodle.org/dev/Survey_2_module#Current_module_status]. &lt;br /&gt;
&lt;br /&gt;
==General features of the Module==&lt;br /&gt;
* translate previously built survey1, feedback and questionnaire at installation time. (The general idea for the module is currently translate at installation time all previous existing survey module instances. Questionnaire and feedback instances can be translated at will using an upgrade helper)&lt;br /&gt;
* upload exported feedback or questionnaires (survey1 doesn&#039;t export questionnaires templates)&lt;br /&gt;
* (available) save instances of survey2 as a template to reuse it or export it&lt;br /&gt;
* (available) import saved survey2 templates&lt;br /&gt;
* (partially available) support for groups and groupings&lt;br /&gt;
* (available custom css) custom user survey2 page layout (custom html and css, as it already is in database module)&lt;br /&gt;
* (available) download of submissions in txt, xls and ods&lt;br /&gt;
* (available) conditional branching&lt;br /&gt;
* (available) handle more than one input form template (and find a way to allow this or that layout to this or that user).&lt;br /&gt;
* (available) order survey fields in editing mode&lt;br /&gt;
* (available) group fields in the page layout with fieldset&lt;br /&gt;
* (available) relations between tables (Example: one record for the profile of my company one related record for each intervention request submitted by my company)&lt;br /&gt;
* (available) email submissions to students/teachers/both/none&lt;br /&gt;
* email submissions to address specified in a designated question field (for surveys not requiring login)&lt;br /&gt;
* indication that students are currently taking (but haven&#039;t completed) the questionnaire&lt;br /&gt;
* (available) for text fields, simple data checking (&#039;must be numeric&#039;, &#039;must contain X character&#039;, &#039;must have exact length n&#039;, etc.)&lt;br /&gt;
* (available) full web accessibility features to the same level as elsewhere in Moodle e.g. labels on text fields, fieldsets on groups of radio buttons &amp;amp; checkboxes.&lt;br /&gt;
* gradebook integration (to allow simple polls to control conditional activities)&lt;br /&gt;
** assigning grades to specific answers to specific questions in a survey&lt;br /&gt;
** may only work for certain question types (e.g. radio buttons, rating)&lt;br /&gt;
** once a student has a grade for their survey completion, that grade can be used elsewhere in the system to control activity display.  usecase - simple poll with 1 question &amp;quot;what are you planning to study next&amp;quot;, 5 radio buttons, each answer a different course assigned grade 20%, 40%, 60%, 80%, 100%.  Then 5 conditional activities set up, for if grade &amp;lt;21%, &amp;lt;41% etc and each one has content specific to the course chosen in the radio button providing basic info on that subject.   Or similarly if you have different focus areas in your course that students can self-select into/swap between a more complex survey could be used with this to decide which focus area to put students into.&lt;br /&gt;
* improve templates (to allow impact views and impressions)&lt;br /&gt;
* enhance the editing&lt;br /&gt;
* ability to have public surveys that do not require users to be logged in&lt;br /&gt;
* would be great for learners to be able to export using Portfolio Export. This would enable things such as self-assessments in Mahara.&lt;br /&gt;
&lt;br /&gt;
==Instance settings page==&lt;br /&gt;
In the moudle instance settings page, the following fine grained control over the record submitted will be an optional Admin setting, so standard users with standard access to recor submitted can just work as usual. &lt;br /&gt;
* Access section: 20 types of survey, distinguished by:&lt;br /&gt;
:-&amp;gt; ppl who is allowed to R/O,&lt;br /&gt;
:-&amp;gt; ppl who is allowed to R/W,&lt;br /&gt;
:-&amp;gt; ppl who is allowed to delete a record&lt;br /&gt;
The reationale is: once a record has been submitted by a user, who is allowed to see it (R/O access)?, who is allowed to edit it (R/W access)?, who is allowed to delete it?&lt;br /&gt;
&lt;br /&gt;
Combining all the options the come out 20 cases are defined as follows where:&lt;br /&gt;
:ALL means, all the people accessing the survey;&lt;br /&gt;
:GROUP means people belonging to the group of the user who submitted the record;&lt;br /&gt;
:OWNER is the user who submitted the record;&lt;br /&gt;
:NONE is none.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! type&lt;br /&gt;
! R/O&lt;br /&gt;
! R/W&lt;br /&gt;
! delete&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| ALL&lt;br /&gt;
| ALL&lt;br /&gt;
| ALL&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| ALL&lt;br /&gt;
| ALL&lt;br /&gt;
| GROUP&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| ALL&lt;br /&gt;
| ALL&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| ALL&lt;br /&gt;
| ALL&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| ALL&lt;br /&gt;
| GROUP&lt;br /&gt;
| GROUP&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| ALL&lt;br /&gt;
| GROUP&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| ALL&lt;br /&gt;
| GROUP&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| ALL&lt;br /&gt;
| OWNER&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| ALL&lt;br /&gt;
| OWNER&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| ALL&lt;br /&gt;
| NONE&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| GROUP&lt;br /&gt;
| GROUP&lt;br /&gt;
| GROUP&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| GROUP&lt;br /&gt;
| GROUP&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| GROUP&lt;br /&gt;
| GROUP&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| GROUP&lt;br /&gt;
| OWNER&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| GROUP&lt;br /&gt;
| OWNER&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| GROUP&lt;br /&gt;
| NONE&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 17&lt;br /&gt;
| OWNER&lt;br /&gt;
| OWNER&lt;br /&gt;
| OWNER&lt;br /&gt;
|-&lt;br /&gt;
| 18&lt;br /&gt;
| OWNER&lt;br /&gt;
| OWNER&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 19&lt;br /&gt;
| OWNER&lt;br /&gt;
| NONE&lt;br /&gt;
| NONE&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| NONE&lt;br /&gt;
| NONE&lt;br /&gt;
| NONE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* option: allow/deny save without submission (save and restart)&lt;br /&gt;
* option: anonymous survey or named responses&lt;br /&gt;
* option: allow access to records any time/after you&#039;ve submitted/after close date/never&lt;br /&gt;
* opening and closing submission dates&lt;br /&gt;
* number of maximun allowed submission&lt;br /&gt;
* option whether to show results immediately after submission, or a thank you message with link to results.&lt;br /&gt;
&lt;br /&gt;
==Field level settings==&lt;br /&gt;
* type of field (char(n), text, number, alphanumeric, boolean, date, picture, file, email, url...) with type check at submit time (and number of digit check for char(n) fields). This information may not be used at field definition time but is useful for data verification/check.&lt;br /&gt;
&lt;br /&gt;
(Example: Please, enter your card ID: _______ This field should be defined, for instance, as char(7))&lt;br /&gt;
* option: mandatory/non mandatory field&lt;br /&gt;
* free text description field for further description and advices to the completer&lt;br /&gt;
*define a range of valid answers for fields allowing this (see next examples)&lt;br /&gt;
(Example 1:&lt;br /&gt;
Please, enter your seniority. (limited between 0 and 50 years)&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
Date of birth: (limited between 18 and 100 years ago))&lt;br /&gt;
*define fields default to be loaded at &amp;quot;new record&amp;quot; display time&lt;br /&gt;
*optional &amp;quot;other&amp;quot; text field for drop down menu/radio button/check box. (see next example)&lt;br /&gt;
(Example for drop down:&lt;br /&gt;
&lt;br /&gt;
 Where were you born? &amp;lt;code&amp;gt;&amp;lt;select name=&amp;quot;dropdown_14&amp;quot; size=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;option value=&amp;quot;1&amp;quot; selected=&amp;quot;selected&amp;quot;&amp;gt;Spain&amp;lt;/option&amp;gt;&amp;lt;option value=&amp;quot;2&amp;quot; &amp;gt;France&amp;lt;/option&amp;gt;&amp;lt;option value=&amp;quot;3&amp;quot; &amp;gt;other, please specify&amp;lt;/option&amp;gt;&amp;lt;/select&amp;gt;  &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;dropdown_14_other&amp;quot; size=&amp;quot;10&amp;quot; maxlength=&amp;quot;10&amp;quot; value=&amp;quot;&amp;quot; /&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you choose &amp;quot;other&amp;quot; in the drop down menu the field will be enabled and become mandatory, otherwise it will be disabled.&lt;br /&gt;
)&lt;br /&gt;
* custom question numbers&lt;br /&gt;
* question name to name columns header in the downloaded document&lt;br /&gt;
&lt;br /&gt;
==Question types==&lt;br /&gt;
&lt;br /&gt;
Question types should be plug-ins so that they can be extended locally if required.  The following list covers types which exist in at least one of the current survey modules.&lt;br /&gt;
&lt;br /&gt;
* radio buttons (horizontal &amp;amp; vertical display)&lt;br /&gt;
* short text entry&lt;br /&gt;
* long text entry&lt;br /&gt;
* checkbox&lt;br /&gt;
* drop down menu&lt;br /&gt;
* customisable likert scale for rating&lt;br /&gt;
* date (Example: When were you born?)&lt;br /&gt;
&lt;br /&gt;
===New question types===&lt;br /&gt;
* short date (with month and years only, to answer question like: When had you the first evidence of this disease?)&lt;br /&gt;
* time&lt;br /&gt;
(Example:&lt;br /&gt;
When do you usually take breakfast?&lt;br /&gt;
)&lt;br /&gt;
* &amp;quot;static text&amp;quot;/&amp;quot;read only&amp;quot; fields (auto filled by the software) like, current_date, user_name, record_ID, counter...&lt;br /&gt;
* allocation (Drag and drop)&lt;br /&gt;
* ranking&lt;br /&gt;
* conditional drop-down where the first list populates the second&lt;br /&gt;
* tabular question with multiple answers, of any depth.&lt;br /&gt;
                                    : Occurrence Quantity   Location&lt;br /&gt;
                                        Bluebird    2       Hobart  &lt;br /&gt;
                                        Jay         1       Kingston&lt;br /&gt;
                                        .&lt;br /&gt;
                                        .&lt;br /&gt;
                                        .&lt;br /&gt;
&lt;br /&gt;
==Result display==&lt;br /&gt;
&lt;br /&gt;
* report per survey, per user and per question&lt;br /&gt;
* report about non-respondent users&lt;br /&gt;
* choose for a question whether to display results as&lt;br /&gt;
** a table&lt;br /&gt;
** a bar chart&lt;br /&gt;
** a pie chart&lt;br /&gt;
&lt;br /&gt;
Would be great to be able to analyse the results according to a particular answer. eg: Show results for just one gender&lt;br /&gt;
&lt;br /&gt;
==Question management==&lt;br /&gt;
&lt;br /&gt;
* questions can be copied within an activity&lt;br /&gt;
* question sets can be created as templates for re-use [maybe later]&lt;br /&gt;
* question sets can be used across multiple courses [maybe later]&lt;br /&gt;
* questions can be re-ordered within an activity&lt;br /&gt;
* question sets can be combined to create a template&lt;br /&gt;
* questions can be deleted from an activity, but there should be an &amp;quot;are you sure&amp;quot; check first.&lt;br /&gt;
&lt;br /&gt;
==Answer Piping &amp;amp; Conditional Questions==&lt;br /&gt;
&lt;br /&gt;
*Allow follow on questions related to the previous.&lt;br /&gt;
*Do You Smoke? Yes/No If Yes is selected a conditional question appears, How Many per Day, if No is selected move on to question 6 appears&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Answer Piping such as:&lt;br /&gt;
*Q1 Which dog breed do you prefer?&lt;br /&gt;
*Labrador&lt;br /&gt;
*Spaniel&lt;br /&gt;
*Collie&lt;br /&gt;
*Other&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Q2 In question 1 you stated you prefer the (Q1 Answer) as a breed, what do you like about it. [Where (Q1 Answer) is replaced with the chosen answer such as &#039;Labrador&#039;&lt;br /&gt;
*Temperament&lt;br /&gt;
*Looks&lt;br /&gt;
*Colour&lt;br /&gt;
*Other&lt;br /&gt;
&lt;br /&gt;
==Current module status==&lt;br /&gt;
The module currently under development is named Collection, so it can be easily deployed over existing installations with almost no impact on current Surveys. For those interested in testing, Daniele Cordella, the main developer of the module, prepared a Github  repo from where you can download the module prototype in it&#039;s current status: https://github.com/kordan/moodle-mod_survey. Please note the module is aimed towards the development branch (master), using it on stable versions such as 2.2 or 2.3 is not supported. Note: prior to install the module, please 1) uninstall standard Moodle Survey module and remove the code from the mod/survey folder 2) replace mod/survey with the code form Github repository 3) visit Admin notification page or launch CLI update script.&lt;br /&gt;
&lt;br /&gt;
==Future plans==&lt;br /&gt;
* community sharing and code refinements&lt;br /&gt;
* add module to contrib plugin as &amp;quot;Survey&amp;quot; to allow extensive testing as well as AMOS translations&lt;br /&gt;
* complete Questionanire and Feedback migration helpers &lt;br /&gt;
* integrate module into core for 2.5 release&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Survey_2&amp;diff=49057</id>
		<title>Survey 2</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Survey_2&amp;diff=49057"/>
		<updated>2015-11-20T12:25:55Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the starting point for finding out about (and joining in with) the project to replace the Survey, Feedback and Questionnaire modules with a new module called simply: Survey.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;I am going to add make unstable surveypro since next monday with further developments.&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;Because of this, anybody wanting to upgrade the surveypro module up to current stable status should be use MOODLE_30_STABLE branch, no matter the Moodle version used (from 2.6.x to 3.0.x).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Since Monday November 23, 2015, master will be for development, and only will work under Moodle 3.0.0 and newer. MOODLE_30_STABLE will be the stable supported branch were fixes will be applied.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Development process==&lt;br /&gt;
&lt;br /&gt;
# Gather requirements from tracker, forums, users ([[Survey_2_brainstorm]])&lt;br /&gt;
# Vote for features and rank them&lt;br /&gt;
# Develop a detailed spec for the first version&lt;br /&gt;
# Develop basic framework for the module&lt;br /&gt;
# Review regularly&lt;br /&gt;
# Develop final module &lt;br /&gt;
# Integrate into core Moodle&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=46182</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=46182"/>
		<updated>2014-08-26T11:27:21Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Verify your installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I set the following fields to these values:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;User Blog&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behavior driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/WAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvars&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
For example, for MAMP you probably need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
# Adding MAMP binaries communicate where CLI is&lt;br /&gt;
PATH=&amp;quot;/Applications/MAMP/bin/php/php5.x.y/bin:$PATH&amp;quot;; export PATH &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
at the end of MAMP/Library/bin/envvars.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
== Browser specific fixes ==&lt;br /&gt;
&lt;br /&gt;
When running acceptance tests in conjunction with Selenium against Chrome, IE, or Safari there is a fix for the navigation bar that gets applied in order to avoid errors arising from a bug in the webdrivers for those browsers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;The issue&amp;lt;/b&amp;gt;: When an acceptance test goes to interact with an element on the page it first ensures that the element in the view-port and if not scrolls the browser to get the element into the view-port. If you are running against Chrome, IE, or Safari the browser scrolls the element only just inside the view-port. It doesn&#039;t however allow for any fixed position elements such as the navigation bar.&lt;br /&gt;
What happens: In some situations these browsers scroll up to reach a button, however not enough as the button ends up behind the navigation bar and cannot be interacted with.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Our solution&amp;lt;/b&amp;gt;: We can not change browser driver behaviour so we have integrated a work around. When running acceptance tests against one of these browsers we change the position attribute of the navigation bar from fixed to absolute. This is not ideal as it is not how the user experiences the site, however it allows us to run the full acceptance test suite against these browsers so we allowed it. A notice will be displayed when you start an acceptance test run if the browser specific fixes have been applied.&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44500</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44500"/>
		<updated>2014-04-18T12:17:53Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Verify your installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/WAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
In MAMP you probably need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
# Adding MAMP binaries communicate where CLI is&lt;br /&gt;
PATH=&amp;quot;/Applications/MAMP/bin/php/php5.x.y/bin:$PATH&amp;quot;; export PATH &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
at the end of MAMP/library/bin/envvars.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44499</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44499"/>
		<updated>2014-04-18T12:15:32Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Verify your installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/WAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
In MAMP you probably need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
# Adding MAMP binaries communicate where CLI is&lt;br /&gt;
PATH=&amp;quot;/Applications/MAMP/bin/php/php5.5.3/bin:$PATH&amp;quot;; export PATH &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
at the end of MAMP/library/bin/envvars.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44498</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44498"/>
		<updated>2014-04-18T11:49:56Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Verify your installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
In MAMP you probably need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
# Adding MAMP binaries communicate where CLI is&lt;br /&gt;
PATH=&amp;quot;/Applications/MAMP/bin/php/php5.5.3/bin:$PATH&amp;quot;; export PATH &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
at the end of MAMP/library/bin/envvars.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44497</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44497"/>
		<updated>2014-04-18T11:49:05Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Verify your installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
In MAMP you probably need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
# Adding MAMP binaries communicate where CLI is&lt;br /&gt;
PATH=&amp;quot;/Applications/MAMP/bin/php/php5.5.3/bin:$PATH&amp;quot;; export PATH &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
at the end of MAMP/library/bin/envvar.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44496</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44496"/>
		<updated>2014-04-18T11:43:37Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Verify your installation ==&lt;br /&gt;
* Test your installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44495</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44495"/>
		<updated>2014-04-18T11:42:52Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Test the right installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44494</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=44494"/>
		<updated>2014-04-18T11:42:04Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and automatically test them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate user interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describe Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;). Each feature file is composed of many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed of steps (for example  &#039;&#039;I press &amp;quot;Post to forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into a PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
These features are executed nightly on the HQ servers using all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and to test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Note that these snippets are only examples and may not work.&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_wwwroot = &#039;http://127.0.0.1&#039;; // must be different from wwwroot&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;behat_wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
** Use $CFG-&amp;gt;behat_wwwroot to set address to be used to access behat instance. It has to be different from $CFG-&amp;gt;wwwroot, you can use for example localhost, 127.0.0.1 or any custom local host name specified in you /etc/hosts. If you use the built-in PHP server use &amp;quot;http://localhost:8000&amp;quot; or the value you set when you started it.&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
* Test the right installation by browsing your /admin/tool/behat/index.php page.&lt;br /&gt;
If you are using MAMP/XAMPP and don&#039;t get the report working... then you need to ensure that your web servers knows where the PHP executable (commad line) is. To do that, edit the &amp;quot;envvar&amp;quot; file (usually under library/bin) and add to $PATH the path to your php executable. Then restart the apache server and try again.&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# (Optional) Start the PHP built-in web server if you configured the $CFG-&amp;gt;behat_wwwroot to use it:&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the test site URL that moodle uses by default, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage or config-dist.php)&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* (See http://www.installationpage.com/selenium/how-to-run-selenium-headless-firefox-in-ubuntu/ for running &#039;headless&#039; Firefox and xvfm in a server environment)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (if you want to prevent access to test environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environment variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are a few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_file_upload&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_alert&#039;&#039;&#039;: All the tests that involves Javascript dialogs (alerts, confirms...) are using a feature that is OS-dependant and out of the browser scope, so they should be tag appropriately as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_window&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; step should be tagged as not all browsers manage them properly.&lt;br /&gt;
* &#039;&#039;&#039;@_switch_iframe&#039;&#039;&#039;: All the tests that are using the &#039;&#039;&#039;I switch to &amp;quot;NAME&amp;quot; window&#039;&#039;&#039; steps should be tagged as it is an advanced feature and some browsers may have problems dealing with them&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Run behat using different browsers ==&lt;br /&gt;
&lt;br /&gt;
Following these instructions you can run behat in Firefox browser through Selenium, but you can run behat using different browsers through Selenium and even use phantomjs (Webkit). [[Acceptance_testing/Browsers|More info]]&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Save screenshots of failures&#039;&#039;&#039;. You can use $CFG-&amp;gt;behat_screenshots_path to specify a directory where behat will generate a screenshot with the browser state each time a scenario fails. This is useful to detect where the problem was and work on a solution.&lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039; option was removed in 2.7, it is recommended to always set $CFG-&amp;gt;behat_wwwroot even in older versions instead.&lt;br /&gt;
* Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
* &#039;&#039;&#039;Extra allowed settings&#039;&#039;&#039;, moodle allows users to define many settings in config.php (see config-dist.php) when running the behat test site those settings are skipped to avoid interaction with the production environment, in case you are interested in allowing some of those extra settings to run the tests using a configuration similar to the one you are using in your production environment you can whitelist them adding them to $CFG-&amp;gt;behat_extraallowedsettings (see config-dist.php for examples).&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Contributing ==&lt;br /&gt;
&lt;br /&gt;
You can contribute the effort to automatically test all of Moodle&#039;s functionalities, please follow the [[contributor guide|https://docs.moodle.org/dev/Acceptance_testing/Contributing_automated_tests]]. Here you can find information about [[Acceptance_testing#Writing_features|how to write new features]] and [[Acceptance_testing#Adding_steps_definitions|how to write new step definitions]] if your changes requires a base change in the [https://github.com/moodlehq/moodle-behat-extension Moodle behat extension] you can find here how to do it following the integration workflow: https://docs.moodle.org/dev/Acceptance_testing/Contributing_to_Moodle_behat_extension.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. &lt;br /&gt;
## The prefixes &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; separates the scenario in 3 parts, the initial context setup (&#039;&#039;Given&#039;&#039;), the action that provokes a change in the system (&#039;&#039;When&#039;&#039;) and the validation of that change outcomes (&#039;&#039;Then&#039;&#039;) So with a quick view at the scenario you can see what it is testing as the prefixes will be something like Given -&amp;gt; And -&amp;gt; And -&amp;gt; When -&amp;gt; And -&amp;gt; And -&amp;gt; Then -&amp;gt; And -&amp;gt; And -&amp;gt; And. Once we begin with the first &#039;&#039;Then&#039;&#039; we can consider that we are checking the outcomes so all the steps from there should be prefixed with &#039;&#039;Then&#039;&#039;&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
* This interface only works on sites where Behat is installed. If you are using the local PHP server, for example, you can access it on that site at http://localhost:8000 (log in as admin/admin).&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
** If your scenario outline consists of many steps it may be useful to add a comment with the number of steps. If the the test fails behat will tell you in which step overall and you will have to divide by the number of steps of the scenario to know in which example. &lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are five methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The five methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A field value&#039;&#039;&#039;; There are many different field types, if an argument requires a field value the expected value will depend on the field type:&lt;br /&gt;
** Text-based fields: It expects the text. This includes textareas, input type text, input type password...&lt;br /&gt;
** Checkbox: It expects 1 to check and for checked and &amp;quot;&amp;quot; to uncheck or for unchecked&lt;br /&gt;
** Select: It expects the option text or the option value. In case you interact with a multi-select you should specify the options separating them with commas. For example: &#039;&#039;&#039;option1, option2, option3&#039;&#039;&#039;&lt;br /&gt;
** Radio: The text of the radio option&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, they always works together with another argument, where you specify the locator (eg. the link text in a link) In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
* &#039;&#039;&#039;A text selector&#039;&#039;&#039;; similar to a selector but those are the elements that returns an area of the DOM, they are useful in steps following the format &#039;&#039;&#039;... in the &amp;quot;Community finder&amp;quot; &amp;quot;block&amp;quot;&#039;&#039;&#039; where you are clicking or looking for some text inside a specific area. In the &#039;Acceptance testing&#039; interface you can see a drop-down menu to select one of these options:&lt;br /&gt;
** dialogue - for searching a dialogue with the specified header text&lt;br /&gt;
** block - for searching a Moodle block by it&#039;s English name or it&#039;s frankenstyle name&lt;br /&gt;
** region - for searching a Moodle page region with that id, in fact it works with all the page&#039;s ids&lt;br /&gt;
** table_row - for searching a table row which contains the specified text&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_file_upload&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exist:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exist:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Activities *(note that this step does not work with all kind of activities, only the ones that have data generators)*&lt;br /&gt;
** The required fields are activity, course and idnumber&lt;br /&gt;
** Uses activity to specify the activity type&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Other activity-dependant fields can be specified by it&#039;s field name&lt;br /&gt;
  Given the following &amp;quot;activities&amp;quot; exist:&lt;br /&gt;
    | activity | course | idnumber | name                 | intro                       |&lt;br /&gt;
    | assign   | C1     | assign1  | Test assignment name | Test assignment description |&lt;br /&gt;
    | data     | C1     | data1    | Test database name   | Test database description   |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exist:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exist:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exist:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exist:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* Roles&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses a one of the following roles as archetype: manager, coursecreator, editingteacher, teacher, student, guest, user and frontpage&lt;br /&gt;
  Given the following &amp;quot;roles&amp;quot; exist:&lt;br /&gt;
    | shortname | name          | description | archetype      |&lt;br /&gt;
    | custom1   | Custom Role 1 |             | editingteacher |&lt;br /&gt;
&lt;br /&gt;
* Role assigns&lt;br /&gt;
** The required fields are user, role, contextlevel and reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;role assigns&amp;quot; exist:&lt;br /&gt;
    | user  | role           | contextlevel | reference |&lt;br /&gt;
    | user1 | manager        | System       |           |&lt;br /&gt;
    | user2 | editingteacher | Category     | CATEGORY1 |&lt;br /&gt;
    | user3 | editingteacher | Course       | COURSE1   |&lt;br /&gt;
&lt;br /&gt;
* System role assigns (deprecated in favour of role assigns, see item right above this one)&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exist:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Permission overrides&lt;br /&gt;
** The required fields are capability, permissions, role, and the contextlevel + it&#039;s reference&lt;br /&gt;
** Uses contextlevel + reference to specify the context. See [[#Referencing_contexts]] for more info.&lt;br /&gt;
  Given the following &amp;quot;permission overrides&amp;quot; exist:&lt;br /&gt;
    | capability            | permission | role           | contextlevel | reference |&lt;br /&gt;
    | mod/forum:editanypost | Allow      | student        | Course       | C1        |&lt;br /&gt;
    | mod/forum:replynews   | Prevent    | editingteacher | Course       | C1        |&lt;br /&gt;
    | mod/paquiro:sings     | Prohibit   | student        | System       |           |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exist:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exist:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exist:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
==== Referencing contexts ====&lt;br /&gt;
&lt;br /&gt;
Moodle has different context levels, internally they have an identifier, but to reference them from steps we can use a more human way, using the level of the context (as specified below) and the reference, which will depend on the contextlevel we are using:&lt;br /&gt;
* contextlevel: &#039;&#039;&#039;System&#039;&#039;&#039;, &#039;&#039;&#039;User&#039;&#039;&#039;, &#039;&#039;&#039;Category&#039;&#039;&#039;, &#039;&#039;&#039;Course&#039;&#039;&#039; and &#039;&#039;&#039;Activity Module&#039;&#039;&#039;&lt;br /&gt;
* reference:&lt;br /&gt;
** System: Nothing, just leave the the cell empty&lt;br /&gt;
** User: The user &#039;&#039;&#039;username&#039;&#039;&#039;&lt;br /&gt;
** Category: The category &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
** Course: The course &#039;&#039;&#039;shortname&#039;&#039;&#039;&lt;br /&gt;
** Activity module: The activity &#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The data generators which makes use of these format are pointing to here.&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag according to https://docs.moodle.org/dev/Acceptance_testing#Tests_filters&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * This second comment line will be not shown in the steps definitions list as it is only&lt;br /&gt;
       * reading the first line in the comment block.&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;username&#039;) . &#039;&amp;quot; with &amp;quot;&#039; . $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;&#039; . get_string(&#039;password&#039;) . &#039;&amp;quot; with &amp;quot;&#039;. $this-&amp;gt;escape($username) . &#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;&#039; . get_string(&#039;login&#039;) . &#039;&amp;quot;&#039;)&lt;br /&gt;
          );&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== How can you tell if Selenium is running? ===&lt;br /&gt;
&lt;br /&gt;
Try going to http://localhost:4444/selenium-server/. If Selenium is not running, nothing will happen. You will get a time-out. If selenium is running, you will get a 404 error page saying powered by Jetty at the bottom.&lt;br /&gt;
&lt;br /&gt;
=== How can I stop Selenium? ===&lt;br /&gt;
&lt;br /&gt;
Go to the URL http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer. I think when it works it outputs &amp;quot;OKOK&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Behat tells me that lots of steps are undefined ===&lt;br /&gt;
&lt;br /&gt;
You probably forgot the --config option in the behat command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also == &lt;br /&gt;
&lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44120</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44120"/>
		<updated>2014-03-08T19:57:54Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Step 2. Create event class */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;eventEVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;eventEVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_logdata() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Installation&amp;quot; of events ==&lt;br /&gt;
&lt;br /&gt;
Please never forget that you have to bump the plugin version in order to get all the new events &amp;quot;installed&amp;quot;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44088</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44088"/>
		<updated>2014-03-06T09:25:23Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Validation and testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;eventEVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;eventEVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Installation&amp;quot; of events ==&lt;br /&gt;
&lt;br /&gt;
Please never forget that you have to bump the plugin version in order to get all the new events &amp;quot;installed&amp;quot;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44087</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44087"/>
		<updated>2014-03-06T09:23:40Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Validation and testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;eventEVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;eventEVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;br /&gt;
&lt;br /&gt;
Please never forget that you have to bump the plugin version in order to get all the new events &amp;quot;installed&amp;quot;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44055</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44055"/>
		<updated>2014-03-04T12:11:49Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Step 2. Create event class */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;eventEVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;eventEVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44054</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44054"/>
		<updated>2014-03-04T12:11:28Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Step 1. Choose a name for the event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;eventEVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;event_EVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44053</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44053"/>
		<updated>2014-03-04T10:28:45Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Step 2. Create event class */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;event_EVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;event_EVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44052</id>
		<title>Migrating logging calls in plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Migrating_logging_calls_in_plugins&amp;diff=44052"/>
		<updated>2014-03-04T09:27:28Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Step 1. Choose a name for the event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is aimed to assist developers in replacing existing &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; and &#039;&#039;&#039;events_trigger()&#039;&#039;&#039; calls with events. This can be implemented in Moodle 2.6 and will be required in 2.7.&lt;br /&gt;
&lt;br /&gt;
As a quick reminder: [[Event 2|new events]] were introduced in Moodle 2.6, a new [[Logging 2|logging system]] is being introduced in Moodle 2.7. The &#039;&#039;&#039;add_to_log()&#039;&#039;&#039; function will be deprecated, but the existing log table will still be present with existing data intact. This original logging is now called &#039;&#039;legacy logging&#039;&#039;. The new and legacy logging may coexist in the legacy logging system for purposes of transition, but this is not recommended for performance reasons. When replacing calls to add_to_log() with the triggering of an event, developers must ensure that they also generate an entry for the legacy log. It will only be used if the legacy log is enabled, since it may be enabled on systems that continue to use custom reports relying on presence of the legacy log table and it may take time to migrate such reports.&lt;br /&gt;
&lt;br /&gt;
== Quick guide ==&lt;br /&gt;
&lt;br /&gt;
If you are replacing common add_to_log() calls such as &amp;quot;view&amp;quot; and &amp;quot;view all&amp;quot; in mod/XXX/view.php and mod/XXX/index.php, see below. Otherwise do the following.&lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choose a name for the event ===&lt;br /&gt;
&lt;br /&gt;
Names should follow the syntax OBJECT_VERB, for example &amp;quot;entry_added&amp;quot;, &amp;quot;work_submitted&amp;quot;, etc. It does not need to include a plugin name because this can be obtained from the PHP class namespace. See [[Event 2|the events documentation]] for more details about events names.&lt;br /&gt;
&lt;br /&gt;
Define a language string for the event name in &#039;&#039;&#039;YOURPLUGINDIR/lang/en/FULLPLUGINNAME.php&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #DFF&amp;quot;&amp;gt;&lt;br /&gt;
$string[&#039;event_EVENTNAME] = &#039;Something has happened&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Create event class ===&lt;br /&gt;
&lt;br /&gt;
For each event you must create an event class in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/EVENTNAME.php&#039;&#039;&#039;, with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class EVENTNAME extends \core\event\base {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;crud&#039;] = &#039;c&#039;; // c(reate), r(ead), u(pdate), d(elete)&lt;br /&gt;
        $this-&amp;gt;data[&#039;edulevel&#039;] = self::LEVEL_PARTICIPATING;&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;...&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_name() {&lt;br /&gt;
        return get_string(&#039;eventEVENTNAME&#039;, &#039;FULLPLUGINNAME&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_description() {&lt;br /&gt;
        return &amp;quot;User {$this-&amp;gt;userid} has ... ... ... with id {$this-&amp;gt;objectid}.&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_url() {&lt;br /&gt;
        return new \moodle_url(&#039;....&#039;, array(&#039;parameter&#039; =&amp;gt; &#039;value&#039;, ...));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function get_legacy_log_data() {&lt;br /&gt;
        // Override if you are migrating an add_to_log() call.&lt;br /&gt;
        return array($this-&amp;gt;courseid, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;,&lt;br /&gt;
            &#039;...........&#039;,&lt;br /&gt;
            $this-&amp;gt;objectid, $this-&amp;gt;contextinstanceid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static function get_legacy_eventname() {&lt;br /&gt;
        // Override ONLY if you are migrating events_trigger() call.&lt;br /&gt;
        return &#039;MYPLUGIN_OLD_EVENT_NAME&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function get_legacy_eventdata() {&lt;br /&gt;
        // Override if you migrating events_trigger() call.&lt;br /&gt;
        $data = new \stdClass();&lt;br /&gt;
        $data-&amp;gt;id = $this-&amp;gt;objectid;&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;relateduserid;&lt;br /&gt;
        return $data;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Trigger the event instead of add_to_log() ===&lt;br /&gt;
&lt;br /&gt;
Replace the add_to_log() with an event trigger. The following is a common example of an event trigger inside an activity module.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
add_to_log($course-&amp;gt;id, &#039;PLUGINNAME&#039;, &#039;LOGACTION&#039;, &#039;...........&#039;, $objid, $cmid);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...becomes...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\EVENTNAME::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $objid,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_module::instance($cmid)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log() to report a &#039;view&#039; event are usually found in mod/PLUGINNAME/view.php (or in a lib function included by this file) and indicate that a user viewed the module. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_viewed.php&#039;&#039;&#039; with the following format.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_viewed extends \core\event\course_module_viewed {&lt;br /&gt;
    protected function init() {&lt;br /&gt;
        $this-&amp;gt;data[&#039;objecttable&#039;] = &#039;PLUGINNAME&#039;;&lt;br /&gt;
        parent::init();&lt;br /&gt;
    }&lt;br /&gt;
    // You might need to override get_url() and get_legacy_log_data() if view mode needs to be stored as well.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
This example takes data from $PAGE object but you may substitute this with ids and objects that you have fetched.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_viewed::create(array(&lt;br /&gt;
    &#039;objectid&#039; =&amp;gt; $PAGE-&amp;gt;cm-&amp;gt;instance,&lt;br /&gt;
    &#039;context&#039; =&amp;gt; $PAGE-&amp;gt;context,&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;add_record_snapshot(&#039;course&#039;, $PAGE-&amp;gt;course);&lt;br /&gt;
$event-&amp;gt;add_record_snapshot($PAGE-&amp;gt;cm-&amp;gt;modname, $activityrecord); // You can use $PAGE-&amp;gt;activityrecord if you have set it or skip this line if you don&#039;t have a record.&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Replacing &#039;view all&#039; events in modules ==&lt;br /&gt;
&lt;br /&gt;
Calls to add_to_log using &#039;view_all&#039; are usually found in mod/PLUGINNAME/index.php (or in a lib function included by this file). These invents indicate that a user viewed the list of all instances of this module within the course. &lt;br /&gt;
&lt;br /&gt;
=== Step 1. Choosing the name ===&lt;br /&gt;
&lt;br /&gt;
Because this is a common event, the name is already chosen: &#039;&#039;&#039;course_module_instance_list_viewed&#039;&#039;&#039; and the language string is defined in core.&lt;br /&gt;
&lt;br /&gt;
=== Step 2. Defining class ===&lt;br /&gt;
&lt;br /&gt;
You must create a class for this event in &#039;&#039;&#039;YOURPLUGINDIR/classes/event/course_module_instance_list_viewed.php&#039;&#039;&#039; with the following structure.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FFA&amp;quot;&amp;gt;&lt;br /&gt;
namespace FULLPLUGINNAME\event;&lt;br /&gt;
defined(&#039;MOODLE_INTERNAL&#039;) || die();&lt;br /&gt;
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3. Triggering the event ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background: #FDD&amp;quot;&amp;gt;&lt;br /&gt;
$event = \FULLPLUGINNAME\event\course_module_instance_list_viewed::create(array(&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_course::instance($course-&amp;gt;id)&lt;br /&gt;
));&lt;br /&gt;
$event-&amp;gt;trigger();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to include in the event ==&lt;br /&gt;
&lt;br /&gt;
=== init() and create() ===&lt;br /&gt;
&lt;br /&gt;
Ideally all information needed when initialising and triggering events should already be available without having to run additional queries. Queries run to fill event objects with data will cause additional performance load, which should be avoided. Information that needs to be gathered from the database should be provided by other event methods, which can be called selectively when needed.&lt;br /&gt;
&lt;br /&gt;
As you noticed in the examples above you can specify additional properties either by the overriding the init() method of the event or when calling create(). The first way is used for properties that are always the same for this event, the second is for dynamic properties that may differ when the event is triggered.&lt;br /&gt;
&lt;br /&gt;
Usually you need to include the following properties.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;context&#039;&#039;&#039; or &#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes the context where the event took place.&lt;br /&gt;
| If you want to hardcode the system context, do so in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;crud&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| Describes whether the event reflects creation (c), reading (r), updating (u) or deleting (d). This should be a single character string.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;edulevel&#039;&#039;&#039;&lt;br /&gt;
| required&lt;br /&gt;
| The level of educational value of the event. Can be LEVEL_TEACHING, LEVEL_PARTICIPATING or LEVEL_OTHER.&lt;br /&gt;
| Most often are specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;objecttable&#039;&#039;&#039; and &#039;&#039;&#039;objectid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Used by events that show the change in one record of one table, which will be the case for the most events.&lt;br /&gt;
| Since &#039;objecttable&#039; is always the same it is usually specified in init().&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;relateduserid&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| The id of the user affected by the event.&lt;br /&gt;
| Only used if it is easy to identify a single user who is affected by this operation. For example a user who is being graded, a user who receives the message, a user being enrolled, etc. &#039;&#039;&#039;This is NOT the user who performs the action&#039;&#039;&#039; (who is identified in the userid field).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;other&#039;&#039;&#039;&lt;br /&gt;
| &lt;br /&gt;
| Everything else that you may think is important about this event. This property will be serialised and stored by loggers.&lt;br /&gt;
| Include only necessary information. It can be used in get_description(), get_url() and get_legacy_logdata(). This property should only contain an array or scalar value, it &#039;&#039;&#039;can not use objects&#039;&#039;&#039;.&lt;br /&gt;
Example of information stored in &#039;other&#039; can be found in event course_module_deleted:&lt;br /&gt;
* Definition: https://github.com/moodle/moodle/blob/master/lib/classes/event/course_module_deleted.php#L69&lt;br /&gt;
* Triggering: https://github.com/moodle/moodle/blob/master/course/lib.php#L1716..L1727&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Usually you don&#039;t need to include the following properties as they are deduced by the base class.&lt;br /&gt;
* &#039;userid&#039;: user who performs the action, taken from $USER&lt;br /&gt;
* &#039;courseid&#039;: course affected in the operation, which will be taken from context. It need not be specified at all for events that are not related to a particular course.&lt;br /&gt;
&lt;br /&gt;
See the [[Event_2#Information_contained_in_events|full list of properties]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== get_legacy_logdata() ===&lt;br /&gt;
&lt;br /&gt;
This method is used to add log data to the legacy log. You need only override this method when replacing an add_to_log() call. Since this document is a transition guide from add_to_log() to events, you will most likely need to override this method. This method needs to return an array (with 3-7 elements) that imitates the arguments that used to be passed to the add_to_log() function. From Moodle 2.7, the get_legacy_logdata() method will only be called if legacy logging is enabled through the legacy logging plugin.&lt;br /&gt;
&lt;br /&gt;
=== get_description() and get_url() ===&lt;br /&gt;
&lt;br /&gt;
Most reporting tools will display aggregated event information (for example the count of student logins) so those methods, which describe individual events, are not likely to be called often; they will only be used by detailed reports such as loglive. At the moment, use get_description() to provide a very brief internal description of the action performed, so that it can be used for error recovery like any other system log. The description is hard-coded in English but it may be possible that future versions of Moodle (2.8 or later) will allow the use of translatable language strings. These methods should not make DB queries, access global variables, etc. For example, when a course is renamed or when a user is deleted, do not retrieve the course name or user name, instead simply use their ids. These functions should return exactly the same result whenever they are called, regardless of the environment or state, even after they have been restored from logs. &lt;br /&gt;
&lt;br /&gt;
=== get_legacy_eventname() and get_legacy_eventdata() ===&lt;br /&gt;
&lt;br /&gt;
You will need to override these two functions if you are upgrading events_trigger() calls. These will allow legacy plugins to continue to listen to your new events without upgrading their listeners.&lt;br /&gt;
&lt;br /&gt;
If you need to provide more detailed information to observers, you can choose to:&lt;br /&gt;
* add more information to &#039;other&#039;, but remember that this will be logged and it&#039;s better to keep logs as small as possible;&lt;br /&gt;
* use record snapshots, which are especially useful for delete actions (you can call get_record_snapshot() inside get_legacy_eventdata() and observers are encouraged to get data from snapshots as well);&lt;br /&gt;
* add new properties to your event class and define getter/setter functions, for example set_custom_data() and get_custom_data().&lt;br /&gt;
&lt;br /&gt;
=== add_record_snapshot() ===&lt;br /&gt;
&lt;br /&gt;
A record snapshot can be added for any DB table related to the event. If it is added it must be an instance of stdClass containing all fields that are present in the corresponding DB table. You must add a record snapshot when you delete something from database. &#039;&#039;&#039;Record snapshots cannot be used from reports, it is intended for event observers only.&#039;&#039;&#039; Usually observers expect record snapshot identified by &#039;objecttable&#039; and &#039;objectid&#039; but developer may also add snapshot of related tables, i.e. when book chapter is updated the developer may decide to add snapshots of related records in tables book_chapters, book, course_modules and course. &lt;br /&gt;
&lt;br /&gt;
Record snapshots should be added only when you already have an object and do not need to perform any additional DB queries to retrieve it. Otherwise omit it, as the record will be retrieved by get_record_snapshot() automatically, and only if needed. For performance reasons the snapshots are not guaranteed to contain an exact state at the time of event triggering, it may be actually fetched at any time between the triggering of event and its observation.&lt;br /&gt;
&lt;br /&gt;
=== can_view() ===&lt;br /&gt;
&lt;br /&gt;
The future of this function is not yet decided. It was intended as a callback allowing each event to determine whether the current user can see the logged event. But in reality checking various capabilities on a large number of records is a very expensive process. At the moment &#039;&#039;&#039;events do not override this function and it is not used&#039;&#039;&#039;. If you are interested in this method&#039;s future, watch/vote/comment on MDL-44107.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Events DON&#039;Ts ==&lt;br /&gt;
&lt;br /&gt;
Do not put more information in &#039;other&#039; than is needed. For example, do not include a full DB record for delete/create operations or a list of all changed properties in edit operations. If observers are interested in this information, it can requested by calling get_record_snapshot(). Never include large text fields in event data. &#039;&#039;&#039;Please help to keep the log size reasonable.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do NOT use $USER, $COURSE, $PAGE or other global variables when overriding get_* methods (with the exception of get_legacy_eventdata).&lt;br /&gt;
&lt;br /&gt;
Do NOT call $this-&amp;gt;get_record_snapshot() inside the event class (again with the exception of get_legacy_eventdata). If you need additional information for internal functions that cannot be added to existing properties, add it to the &#039;other&#039; property.&lt;br /&gt;
&lt;br /&gt;
Do NOT use $this-&amp;gt;context inside an event class. &#039;&#039;&#039;Remember that methods get_description() and get_url() may be called on events after they have been restored from logs.&#039;&#039;&#039; It is possible that the original context no longer exists when these functions are called. Instead use $this-&amp;gt;contextid, $this-&amp;gt;contextlevel, $this-&amp;gt;contextinstanceid.&lt;br /&gt;
&lt;br /&gt;
== Validation and testing ==&lt;br /&gt;
&lt;br /&gt;
You may notice that the most of events in Moodle also have function validate_data() . You can add this function for your own safety to ensure that you don&#039;t forget to define all required data when triggering event.&lt;br /&gt;
&lt;br /&gt;
We highly recommend to cover your events with unit tests. Search in standard plugins for files with the names events_test.php to see examples.&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:recurrence.png&amp;diff=41009</id>
		<title>File:recurrence.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:recurrence.png&amp;diff=41009"/>
		<updated>2013-06-25T06:02:44Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Survey_element_types&amp;diff=41008</id>
		<title>Survey element types</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Survey_element_types&amp;diff=41008"/>
		<updated>2013-06-25T06:02:13Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* Questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Elements building a survey are divided in two types: &amp;quot;question&amp;quot; or &amp;quot;format&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The semantics which distinguishes between &amp;quot;question&amp;quot; or &amp;quot;format&amp;quot; is this: a &amp;quot;question&amp;quot; is the form element providing what remote user submitted, &amp;quot;format&amp;quot; is ONLY a form aesthetic element.&lt;br /&gt;
&lt;br /&gt;
Questions such as formats differ each other by plugin.&lt;br /&gt;
&lt;br /&gt;
There are some question plugin provided with survey such as some format plugin too.&lt;br /&gt;
&lt;br /&gt;
So hierarchically a survey is done by:&lt;br /&gt;
*Survey form&lt;br /&gt;
** elements&lt;br /&gt;
*** element type questions (shortly called &amp;quot;questions&amp;quot;)&lt;br /&gt;
**** question plugin 1&lt;br /&gt;
**** question plugin 2&lt;br /&gt;
**** question plugin ...&lt;br /&gt;
*** element type formats (shortly called &amp;quot;formats&amp;quot;)&lt;br /&gt;
**** format plugin 1&lt;br /&gt;
**** format plugin 2&lt;br /&gt;
**** format plugin ...&lt;br /&gt;
==Questions==&lt;br /&gt;
&lt;br /&gt;
(Click any screenshot below to see it enlarged.) &lt;br /&gt;
{|&lt;br /&gt;
|[[File:age.png|thumb|&amp;quot;Age&amp;quot; question type]]&lt;br /&gt;
|[[File:autofill.png|thumb|&amp;quot;Autofill&amp;quot; question type]]&lt;br /&gt;
|[[File:boolean.png|thumb|&amp;quot;Boolean&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:checkbox.png|thumb|&amp;quot;Check box&amp;quot; question type]]&lt;br /&gt;
|[[File:date.png|thumb|&amp;quot;Date&amp;quot; question type]]&lt;br /&gt;
|[[File:datetime.png|thumb|&amp;quot;Date and time&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:fileupload.png|thumb|&amp;quot;Attachment&amp;quot; question type]]&lt;br /&gt;
|[[File:integer.png|thumb|&amp;quot;Integer&amp;quot; question type]]&lt;br /&gt;
|[[File:multiselect.png|thumb|&amp;quot;Multi select&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:numeric.png|thumb|&amp;quot;Numeric&amp;quot; question type]]&lt;br /&gt;
|[[File:radiobutton.png|thumb|&amp;quot;Radio button&amp;quot; question type]]&lt;br /&gt;
|[[File:rate.png|thumb|&amp;quot;Rate&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:recurrence.png|thumb|&amp;quot;Recurrence&amp;quot; question type]]&lt;br /&gt;
|[[File:select.png|thumb|&amp;quot;Select&amp;quot; question type]]&lt;br /&gt;
|[[File:shortdate.png|thumb|&amp;quot;Short date&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:shorttext.png|thumb|&amp;quot;Short text&amp;quot; question type]]&lt;br /&gt;
|[[File:textarea.png|thumb|&amp;quot;text area&amp;quot; question type]]&lt;br /&gt;
|[[File:time.png|thumb|&amp;quot;time&amp;quot; question type]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Formats==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[File:fieldset.png|thumb|&amp;quot;Fieldset&amp;quot; format type]]&lt;br /&gt;
|[[File:label.png|thumb|&amp;quot;Label&amp;quot; format type]]&lt;br /&gt;
|[[File:pagebreak.png|thumb|&amp;quot;Page break&amp;quot; format type]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Survey_element_types&amp;diff=41007</id>
		<title>Survey element types</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Survey_element_types&amp;diff=41007"/>
		<updated>2013-06-25T06:00:52Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Elements building a survey are divided in two types: &amp;quot;question&amp;quot; or &amp;quot;format&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The semantics which distinguishes between &amp;quot;question&amp;quot; or &amp;quot;format&amp;quot; is this: a &amp;quot;question&amp;quot; is the form element providing what remote user submitted, &amp;quot;format&amp;quot; is ONLY a form aesthetic element.&lt;br /&gt;
&lt;br /&gt;
Questions such as formats differ each other by plugin.&lt;br /&gt;
&lt;br /&gt;
There are some question plugin provided with survey such as some format plugin too.&lt;br /&gt;
&lt;br /&gt;
So hierarchically a survey is done by:&lt;br /&gt;
*Survey form&lt;br /&gt;
** elements&lt;br /&gt;
*** element type questions (shortly called &amp;quot;questions&amp;quot;)&lt;br /&gt;
**** question plugin 1&lt;br /&gt;
**** question plugin 2&lt;br /&gt;
**** question plugin ...&lt;br /&gt;
*** element type formats (shortly called &amp;quot;formats&amp;quot;)&lt;br /&gt;
**** format plugin 1&lt;br /&gt;
**** format plugin 2&lt;br /&gt;
**** format plugin ...&lt;br /&gt;
==Questions==&lt;br /&gt;
&lt;br /&gt;
(Click any screenshot below to see it enlarged.) &lt;br /&gt;
{|&lt;br /&gt;
|[[File:age.png|thumb|&amp;quot;Age&amp;quot; question type]]&lt;br /&gt;
|[[File:autofill.png|thumb|&amp;quot;Autofill&amp;quot; question type]]&lt;br /&gt;
|[[File:boolean.png|thumb|&amp;quot;Boolean&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:checkbox.png|thumb|&amp;quot;Check box&amp;quot; question type]]&lt;br /&gt;
|[[File:date.png|thumb|&amp;quot;Date&amp;quot; question type]]&lt;br /&gt;
|[[File:datetime.png|thumb|&amp;quot;Date and time&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:fileupload.png|thumb|&amp;quot;Attachment&amp;quot; question type]]&lt;br /&gt;
|[[File:integer.png|thumb|&amp;quot;Integer&amp;quot; question type]]&lt;br /&gt;
|[[File:multiselect.png|thumb|&amp;quot;Multi select&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:numeric.png|thumb|&amp;quot;Numeric&amp;quot; question type]]&lt;br /&gt;
|[[File:radiobutton.png|thumb|&amp;quot;Radio button&amp;quot; question type]]&lt;br /&gt;
|[[File:rate.png|thumb|&amp;quot;Rate&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:select.png|thumb|&amp;quot;Select&amp;quot; question type]]&lt;br /&gt;
|[[File:shortdate.png|thumb|&amp;quot;Short date&amp;quot; question type]]&lt;br /&gt;
|[[File:shorttext.png|thumb|&amp;quot;Short text&amp;quot; question type]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:textarea.png|thumb|&amp;quot;text area&amp;quot; question type]]&lt;br /&gt;
|[[File:time.png|thumb|&amp;quot;time&amp;quot; question type]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Formats==&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[File:fieldset.png|thumb|&amp;quot;Fieldset&amp;quot; format type]]&lt;br /&gt;
|[[File:label.png|thumb|&amp;quot;Label&amp;quot; format type]]&lt;br /&gt;
|[[File:pagebreak.png|thumb|&amp;quot;Page break&amp;quot; format type]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40123</id>
		<title>Rationale</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40123"/>
		<updated>2013-05-23T10:24:05Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* at report level */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The idea standing after the development of this module is to provide answers to the more frequent requirements of end users.&lt;br /&gt;
&lt;br /&gt;
Their frequent requirements are:&lt;br /&gt;
==at element level==&lt;br /&gt;
* possibility to add custom question type matching specific needs&lt;br /&gt;
* question predefined value&lt;br /&gt;
* question level validation&lt;br /&gt;
* reduce, as much as possible, the range of possible answers&lt;br /&gt;
* mandatory questions&lt;br /&gt;
* have some typographic option to display elements in different ways&lt;br /&gt;
* tools to quickly reuse set a specific questions that never change in each survey&lt;br /&gt;
==at survey level==&lt;br /&gt;
* conditional branching&lt;br /&gt;
* force the user to provide a his/her own answer&lt;br /&gt;
* subset of questions available to teachers ONLY&lt;br /&gt;
* include permission management for groups of users submitting surveys in behalf of the same institution&lt;br /&gt;
* use captcha&lt;br /&gt;
* allow/deny submitted survey modification (including deletion)&lt;br /&gt;
* allow a backup copy of submitted surveys each time they are modified in order to preserve their history&lt;br /&gt;
* speed up form fill as much as it is possible&lt;br /&gt;
&lt;br /&gt;
==at report level==&lt;br /&gt;
* possibility to add custom reports&lt;br /&gt;
* export of submitted surveys&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40122</id>
		<title>Rationale</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40122"/>
		<updated>2013-05-23T10:22:52Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* at survey level */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The idea standing after the development of this module is to provide answers to the more frequent requirements of end users.&lt;br /&gt;
&lt;br /&gt;
Their frequent requirements are:&lt;br /&gt;
==at element level==&lt;br /&gt;
* possibility to add custom question type matching specific needs&lt;br /&gt;
* question predefined value&lt;br /&gt;
* question level validation&lt;br /&gt;
* reduce, as much as possible, the range of possible answers&lt;br /&gt;
* mandatory questions&lt;br /&gt;
* have some typographic option to display elements in different ways&lt;br /&gt;
* tools to quickly reuse set a specific questions that never change in each survey&lt;br /&gt;
==at survey level==&lt;br /&gt;
* conditional branching&lt;br /&gt;
* force the user to provide a his/her own answer&lt;br /&gt;
* subset of questions available to teachers ONLY&lt;br /&gt;
* include permission management for groups of users submitting surveys in behalf of the same institution&lt;br /&gt;
* use captcha&lt;br /&gt;
* allow/deny submitted survey modification (including deletion)&lt;br /&gt;
* allow a backup copy of submitted surveys each time they are modified in order to preserve their history&lt;br /&gt;
* speed up form fill as much as it is possible&lt;br /&gt;
&lt;br /&gt;
==at report level==&lt;br /&gt;
* custom reports&lt;br /&gt;
* export of submitted surveys&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40121</id>
		<title>Rationale</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40121"/>
		<updated>2013-05-23T10:20:10Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* at survey level */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The idea standing after the development of this module is to provide answers to the more frequent requirements of end users.&lt;br /&gt;
&lt;br /&gt;
Their frequent requirements are:&lt;br /&gt;
==at element level==&lt;br /&gt;
* possibility to add custom question type matching specific needs&lt;br /&gt;
* question predefined value&lt;br /&gt;
* question level validation&lt;br /&gt;
* reduce, as much as possible, the range of possible answers&lt;br /&gt;
* mandatory questions&lt;br /&gt;
* have some typographic option to display elements in different ways&lt;br /&gt;
* tools to quickly reuse set a specific questions that never change in each survey&lt;br /&gt;
==at survey level==&lt;br /&gt;
* conditional branching&lt;br /&gt;
* force the user to provide a his/her own answer&lt;br /&gt;
* subset of questions available to teachers ONLY&lt;br /&gt;
* include permission management for groups of users submitting surveys in behalf of the same institution&lt;br /&gt;
* use captcha&lt;br /&gt;
* allow/deny submitted survey modification (including deletion)&lt;br /&gt;
* save a backup copy of each submitted survey each time it is modified in order to have the History of the record&lt;br /&gt;
* speed up form fill as much as it is possible&lt;br /&gt;
&lt;br /&gt;
==at report level==&lt;br /&gt;
* custom reports&lt;br /&gt;
* export of submitted surveys&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40120</id>
		<title>Rationale</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40120"/>
		<updated>2013-05-23T10:18:46Z</updated>

		<summary type="html">&lt;p&gt;Kordan: /* at survey level */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The idea standing after the development of this module is to provide answers to the more frequent requirements of end users.&lt;br /&gt;
&lt;br /&gt;
Their frequent requirements are:&lt;br /&gt;
==at element level==&lt;br /&gt;
* possibility to add custom question type matching specific needs&lt;br /&gt;
* question predefined value&lt;br /&gt;
* question level validation&lt;br /&gt;
* reduce, as much as possible, the range of possible answers&lt;br /&gt;
* mandatory questions&lt;br /&gt;
* have some typographic option to display elements in different ways&lt;br /&gt;
* tools to quickly reuse set a specific questions that never change in each survey&lt;br /&gt;
==at survey level==&lt;br /&gt;
* conditional branching&lt;br /&gt;
* force the user to provide a his/her own answer&lt;br /&gt;
* subset of questions available to teachers ONLY&lt;br /&gt;
* a way to allow a set of users to submit the same survey writing it at different times&lt;br /&gt;
* use captcha&lt;br /&gt;
* allow/deny submitted survey modification (including deletion)&lt;br /&gt;
* save a backup copy of each submitted survey each time it is modified in order to have the History of the record&lt;br /&gt;
* speed up form fill as much as it is possible&lt;br /&gt;
&lt;br /&gt;
==at report level==&lt;br /&gt;
* custom reports&lt;br /&gt;
* export of submitted surveys&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:shortdate.png&amp;diff=40119</id>
		<title>File:shortdate.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:shortdate.png&amp;diff=40119"/>
		<updated>2013-05-23T10:09:03Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:shortdate.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:datetime.png&amp;diff=40118</id>
		<title>File:datetime.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:datetime.png&amp;diff=40118"/>
		<updated>2013-05-23T10:08:25Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:datetime.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:time.png&amp;diff=40117</id>
		<title>File:time.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:time.png&amp;diff=40117"/>
		<updated>2013-05-23T10:06:50Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:time.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:textarea.png&amp;diff=40116</id>
		<title>File:textarea.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:textarea.png&amp;diff=40116"/>
		<updated>2013-05-23T10:04:21Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:textarea.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:shorttext.png&amp;diff=40115</id>
		<title>File:shorttext.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:shorttext.png&amp;diff=40115"/>
		<updated>2013-05-23T10:04:10Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:shorttext.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:datetime.png&amp;diff=40114</id>
		<title>File:datetime.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:datetime.png&amp;diff=40114"/>
		<updated>2013-05-23T10:03:42Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:datetime.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:select.png&amp;diff=40113</id>
		<title>File:select.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:select.png&amp;diff=40113"/>
		<updated>2013-05-23T10:03:38Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:select.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:rate.png&amp;diff=40112</id>
		<title>File:rate.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:rate.png&amp;diff=40112"/>
		<updated>2013-05-23T10:03:13Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:rate.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:radiobutton.png&amp;diff=40111</id>
		<title>File:radiobutton.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:radiobutton.png&amp;diff=40111"/>
		<updated>2013-05-23T10:02:51Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:radiobutton.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:numeric.png&amp;diff=40110</id>
		<title>File:numeric.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:numeric.png&amp;diff=40110"/>
		<updated>2013-05-23T10:02:24Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:numeric.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:multiselect.png&amp;diff=40109</id>
		<title>File:multiselect.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:multiselect.png&amp;diff=40109"/>
		<updated>2013-05-23T10:02:09Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:multiselect.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:integer.png&amp;diff=40108</id>
		<title>File:integer.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:integer.png&amp;diff=40108"/>
		<updated>2013-05-23T10:01:19Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:integer.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:fileupload.png&amp;diff=40107</id>
		<title>File:fileupload.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:fileupload.png&amp;diff=40107"/>
		<updated>2013-05-23T10:00:53Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:fileupload.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:checkbox.png&amp;diff=40106</id>
		<title>File:checkbox.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:checkbox.png&amp;diff=40106"/>
		<updated>2013-05-23T10:00:05Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:checkbox.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:boolean.png&amp;diff=40105</id>
		<title>File:boolean.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:boolean.png&amp;diff=40105"/>
		<updated>2013-05-23T09:59:06Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:boolean.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:autofill.png&amp;diff=40104</id>
		<title>File:autofill.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:autofill.png&amp;diff=40104"/>
		<updated>2013-05-23T09:58:44Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:autofill.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:age.png&amp;diff=40103</id>
		<title>File:age.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:age.png&amp;diff=40103"/>
		<updated>2013-05-23T09:58:20Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:age.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:date.png&amp;diff=40102</id>
		<title>File:date.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:date.png&amp;diff=40102"/>
		<updated>2013-05-23T09:55:50Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:date.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:date.png&amp;diff=40100</id>
		<title>File:date.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:date.png&amp;diff=40100"/>
		<updated>2013-05-23T09:52:42Z</updated>

		<summary type="html">&lt;p&gt;Kordan: uploaded a new version of &amp;amp;quot;File:date.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Main_features&amp;diff=40085</id>
		<title>Main features</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Main_features&amp;diff=40085"/>
		<updated>2013-05-23T06:13:35Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The main features of the survey module are:&lt;br /&gt;
&lt;br /&gt;
* branching based on answer given by the users&lt;br /&gt;
* mandatory question answer&lt;br /&gt;
* user entry validation&lt;br /&gt;
* capability based access to survey elements&lt;br /&gt;
* permissions for groups of users feeding the same surveys submission&lt;br /&gt;
* pluggable question types&lt;br /&gt;
* pluggable format types&lt;br /&gt;
* pluggable master templates&lt;br /&gt;
* pluggable reports&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40083</id>
		<title>Rationale</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Rationale&amp;diff=40083"/>
		<updated>2013-05-23T05:34:22Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The idea standing after the development of this module is to provide answers to the more frequent requirements of end users.&lt;br /&gt;
&lt;br /&gt;
Their frequent requirements are:&lt;br /&gt;
==at element level==&lt;br /&gt;
* possibility to add custom question type matching specific needs&lt;br /&gt;
* question predefined value&lt;br /&gt;
* question level validation&lt;br /&gt;
* reduce, as much as possible, the range of possible answers&lt;br /&gt;
* mandatory questions&lt;br /&gt;
* have some typographic option to display elements in different ways&lt;br /&gt;
* tools to quickly reuse set a specific questions that never change in each survey&lt;br /&gt;
==at survey level==&lt;br /&gt;
* conditional branching&lt;br /&gt;
* force the user to provide a his/her own answer&lt;br /&gt;
* subset of questions available to teachers ONLY&lt;br /&gt;
* a way to allow a set of users to submit the same survey writing it at different times&lt;br /&gt;
* use captcha&lt;br /&gt;
* save a backup copy of each submitted survey each time it is modified in order to have the History of the record&lt;br /&gt;
* speed up form fill as much as it is possible&lt;br /&gt;
==at report level==&lt;br /&gt;
* custom reports&lt;br /&gt;
* export of submitted surveys&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:pagebreak.png&amp;diff=40054</id>
		<title>File:pagebreak.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:pagebreak.png&amp;diff=40054"/>
		<updated>2013-05-22T14:44:48Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:label.png&amp;diff=40052</id>
		<title>File:label.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:label.png&amp;diff=40052"/>
		<updated>2013-05-22T14:44:19Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:fieldset.png&amp;diff=40050</id>
		<title>File:fieldset.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:fieldset.png&amp;diff=40050"/>
		<updated>2013-05-22T14:43:54Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:time.png&amp;diff=40048</id>
		<title>File:time.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:time.png&amp;diff=40048"/>
		<updated>2013-05-22T14:43:10Z</updated>

		<summary type="html">&lt;p&gt;Kordan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kordan</name></author>
	</entry>
</feed>