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.

Questions included in Moodle

true false, short answer

simplest question types

Essay question type (essay)

manually not automatically graded.

Calculated question

Uses a multipage question editing form

Multiple choice (multichoice)

there is actually no reason why you should only have a single question_definition class for your question type. You may decide, depending on the question options, to use different definition classes where question options cause questions to behave in different ways. The multiple-choice question type does this, using different definition and renderer classes for single-response and multiple-response questions (but with common subclasses). This is possible because your question type can override the question_type::make_question_instance and question_definition::get_renderer methods.

communicates with an external system by a web service protocol based on SOAP to "delegate the rendering of questions, the scoring of responses and the generation of feedback to a remote question engine." See https://docs.moodle.org/dev/Opaque

Question type plug in template

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.

There is an importnat 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.

Defines any capabilities required by this question type. This is very rarely needed.

version.php

version information for the question type. Also you can included 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

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.

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.

Create some new questions of your type, exercising each significantly different combination of options.

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.

Try typing invalid input into the editing form, and ensure it is rejected.

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.

Add your questions to some quizzes, using a range of behaviours, and different numbers of questions per page.

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.

Now log in as a student and take the quizzes for real.

If the question type uses complex CSS or JavaScript, repeat this testing in different web browsers.

View all of the quiz reports, and ensure that they correctly display the information about your questions.

Backup the course, restore it as a new course, and ensure that all your questions have been copied across accurately.

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).

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.

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.

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.