Note:

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

Question types

From MoodleDocs

Question type plugin development

This page explains how to write a question type that works with the new Moodle question engine, the question engine introduced with Moodle 2.1 .

For instructions on making a question type for versions of Moodle prior to Moodle 2.1, see Question_type_plugin_how_to.

Previous section: Developing a Question Behaviour

Existing question type plugins are helpful guides

To learn to write question types, you are highly encouraged to read the code of some of the existing question types included in the Moodle code base, also there are many more examples of question types in the Open University github repository and even more examples of question types can be found in the question type category of the Moodle plugins database.

Existing question types and some of their features

Below is a list of question types with their features, it is a good idea to examine the code of these question types especially ones that have features you may need for your new question type.

If your question type will be GPLed then of course you can reuse the code in these question types either by cut and paste or by inheriting code from these question types.

Question type plugin template

There is a question type plug in template available on github. See the README.md file on the front page of the repository for more info about this template and how you are recommended to use it.

The question engine itself is well commented

Also note that all the question engine code has extensive PHP documenter comments that should explain the purpose of every class and method. This document is supposed to provide an overview of the key points to get you started. It does not attempt to duplicate all the details in the PHPdocs.

This document assumes that you understand the data model, where a question attempt comprises a number of steps, and each step has some properties like a state and a mark, and an array of submitted data. See the question engine overview doc for background.

Changes in the question type plugin api between Moodle versions

question/type/upgrade.txt details the changes in the question type plug in api between Moodle versions.

File layout

Question types live in a folder in question/type. The layout inside this folder follows the typical layout for a Moodle plugin. For example, inside the question/type/mytype/ folder we would have:

edit_YOURQTYPENAME_form.php
is the moodle_form used to define your question editing form. See [lib/formslib.php_Form_Definition] for details of how to use the formslib api.
questiontype.php
Defines the qtype_mytype class. This must extend question_type.
question.php
This contains the definition of the qtype_mytype_question class, which should extend the question_definition base class.
renderer.php
This contains the definition of the qtype_mytype_renderer class, which should extend the qtype_renderer base class.
tests/...
Contains the unit tests and Behat tests for this question type. You are strongly encouraged to write thorough unit tests to ensure the correctness of your question type.
lang/en/qtype_myqtype.php
English language strings for this question type. You can, of course, include other languages too. The language file must define at least the strings giving the model a name etc., for example :
   $string['pluginname'] = 'YOURQTYPENAME';
   $string['pluginname_help'] = 'Create a .. description of your question type'; // This is the text of the help icon show by the heading above the add/edit form when a question of this type is being edited.
   $string['pluginname_link'] = 'question/type/YOURQTYPENAME';
   $string['pluginnameadding'] = 'Adding a YOURQTYPENAME question';
   $string['pluginnameediting'] = 'Editing a YOURQTYPENAME question';
   $string['pluginnamesummary'] = 'A YOURQTYPENAME question type which allows...'; // This is the text relating to this question type shown in the question-type chooser dialogue.
lib.php
There is an important callback here that is called by the question engine to allow access to files used by questions. As with other plugins, you can put library code here, but in modern Moodle code it is better to use the classes/ folder.
db/install.xml, db/upgrade.php
For creating any database tables required, as normal. See #Database_tables below.
db/access.php
Defines any capabilities required by this question type. This is very rarely needed.
version.php
version information for the question type. Also you can include in here which version no of core Moodle and/or other question types this module requires. Bumping up the question version will trigger the appropriate code in upgrade.php to be run when someone accesses {yourwwwroot}/admin/.
styles.css
contains the styles used by your question type. You should preface all your selectors with .que.YOURQTYPENAME so that the styles are only applied to your question type which is wrapped in a div with class 'que' and 'YOURQTYPENAME'.
pix/icon.svg
is the 16 * 16 px icon that appears next to your question type in the question type selection panel. (Can aslo be .png or .gif.)
backup/moodle2/
contains the files to implement backup and restore for your question type.
classes/
As with any Moodle plugin, you can put any extra classes you want to use to organise your code in here, and they will be auto-loaded.
settings.php (optional)
Admin menu settings that are for every question of this type. See Admin settings for info on how to add a settings page for your question type and the examples in some question types.
amd/
any JavaScript modules your question type needs. (Old YUI-style JavaScript in yui/ or module.js would still work, but is not recommended.)

Database tables

Note that question types should only have database tables for the question definition. All runtime data is managed by the question engine.

Question definitions should use the core table question, and can use the core table question_answers. You only need to define database tables if your question definition requires extra information that cannot be stored in either of these.

Question type and question definition classes

The question definition class is in question.php

This holds the definition of a particular question of this type. For example, an object of type qtype_shortanswer_question is a short-answer question instance. If you load three short-answer questions from the question bank, then you will get three instances of that class. This class is not just the question definition, it can also track the current state of a question as a student attempts it. For example, for a multiple-choice question, the class will store what order the choices have been randomly shuffled into for that student. To put it another way, the question_definition often works with a particular question_attempt.

The question type class is in questiontype.php

In contrast, the question type class represents the question type. For example, the qtype_shortanswer class represents the 'short answer' type of question, and there will normally only be a single instance of this class. It has several responsiblities, including providing meta-data about this question type (e.g. public function name()). It knows how to load, save and delete questions of this type to and from the database (get_question_options and save_question_options, delete_question) and hence how to construct instances of the qtype_shortanswer_question class. It provides methods to help with editing questions of this type. It can also provide the implmentation for import and export in various formats.

Renderer class

Renderers are all about generating HTML output. The overall output of questions is controlled by the core_question_renderer::question(...), which in turn calls other methods of itself, the question type renderer and the behaviour renderer, to generate the various bits of output. See this overview of the parts of a question.

Once again, in what follows, I only list the methods your are most likely to want to override.


formulation_and_controls

This displays the question text, and the controls the student will use to input their response. Some question types choose to display some feedback embedded in this area, for example ticks and crosses next to parts of the student's response.

The output code must respect the question_display_options and only reveal as much information as the calling code wants revealed. For example, the teacher creating a quiz may not want the correct answer revealed until after the quiz close date. Also, when outputting the controls, you need to respect the ->readonly option.

specific_feedback

Anything returned by this method is included in the 'outcome' area of the question, it is some feedback that particularly relates to the response the student gave. This method is only called if the display options allow this to be shows, so you don't have to worry about testing that.


correct_response

This would also be included in the 'outcome' area. This should be an automatically generated (from the question definition) display of what the correct answer to the question is.

Unit tests

For a general introduction, see the Moodle unit testing documentation.

The most important parts of your question type to test are the parts of the question_definition class that process the students responses, including the compare and grade methods. The best way to start is probably to look at the tests for some of the standard question types.

Another useful type of test for questions are 'walkthrough' tests. These are implemented in using PHPunit, even though they are more integration tests than unit tests. There is a detailed comment on the base class that explains more and there are some nice examples in the core questions and also in the OU Multi response github repository.

Finally, it is good to have a few Behat tests, to check that everything works end-to-end. However, since Behat is much slower than unit tests, it is best to test all the details of the grading in PHPunit.

Manually testing a question type

The following provides a thorough test of a new question type. A lot of the following can now be done with Behat.

  1. Create some new questions of your type, exercising each significantly different combination of options.
  2. Go back to the editing form for each question to make sure that the options were saved accurately and that you can change them and that the changes are then saved.
  3. Try typing invalid input into the editing form, and ensure it is rejected.
  4. Preview each of your new questions using a variety of question behaviours. Check the mark and feedback for a range of correct and incorrect responses.
  5. Add your questions to some quizzes, using a range of behaviours, and different numbers of questions per page.
  6. Preview those quizzes several times, entering a range of correct and incorrect responses. Note that during this testing, some useful behind-the-scenes data (e.g. question summary, the exact behaviour being used) is shown in the Technical information region, so you probably want to expand that region, and check what is going on there.
  7. Now log in as a student and take the quizzes for real.
  8. If the question type uses complex CSS or JavaScript, repeat this testing in different web browsers.
  9. View all of the quiz reports, and ensure that they correctly display the information about your questions.
  10. Backup the course, restore it as a new course, and ensure that all your questions have been copied across accurately.
  11. Export your questions from the original course to each of the import/export formats your support. Import these questions to a new question category, and ensure they have been copied accurately (within the limits to which your question type can be represented in each format).
  12. Edit the questions so that there is a link to another part of the course (e.g. a Page resource) in every place that HTML can be entered. Repeat the backup and restore test and verify that the URLs in the restored question have been updated to point to the new copy of the Page resource in the new course.
  13. Edit the question to add an image to every HTML field that supports it. Repeat the Editing, Preview, Backup/restore and Export/import tests, and verify that the images always work, and that you don't end up with broken image links.
  14. Move the category containing the questions with images to a new context in the question bank, and make sure that the image links don't break.
  15. Check that all the unit tests for your question type pass.
  16. Run code-checker (https://moodle.org/plugins/view.php?plugin=local_codechecker) over your plugin to check the coding style.
  17. Check your question type against Accessibility guidelines, for example the Web Content Accessibility Guidelines.

See also

Next section: Using the question engine from a module.

  • The PHP documenter comments that explain the purposes of every method in the question engine code.
  • Back to Question Engine 2