Permalink
Browse files

MDL-38538 question unit tests: improve things a bit.

1. Split the question_attempt tests into one class per file.
2. Imporve the API to give tests more control, and to test more of the
   important code. Some of this is not used here, but it is about to be.
  • Loading branch information...
timhunt committed Mar 28, 2013
1 parent d8201d4 commit eca230b52197bec87bee059d15651a3886dd62f8
@@ -49,6 +49,40 @@ public function set_behaviour(question_behaviour $behaviour) {
}
+/**
+ * Test subclass to allow access to some protected data so that the correct
+ * behaviour can be verified.
+ *
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_question_engine_unit_of_work extends question_engine_unit_of_work {
+ public function get_modified() {
+ return $this->modified;
+ }
+
+ public function get_attempts_added() {
+ return $this->attemptsadded;
+ }
+
+ public function get_attempts_modified() {
+ return $this->attemptsmodified;
+ }
+
+ public function get_steps_added() {
+ return $this->stepsadded;
+ }
+
+ public function get_steps_modified() {
+ return $this->stepsmodified;
+ }
+
+ public function get_steps_deleted() {
+ return $this->stepsdeleted;
+ }
+}
+
+
/**
* Base class for question type test helpers.
*
@@ -649,14 +683,50 @@ protected function start_attempt_at_question($question, $preferredbehaviour,
$this->slot = $this->quba->add_question($question, $maxmark);
$this->quba->start_question($this->slot, $variant);
}
+
protected function process_submission($data) {
- $this->quba->process_action($this->slot, $data);
+ // Backwards compatibility.
+ reset($data);
+ if (count($data) == 1 && key($data) === '-finish') {
+ $this->finish();
+ }
+
+ $prefix = $this->quba->get_field_prefix($this->slot);
+ $fulldata = array(
+ 'slots' => $this->slot,
+ $prefix . ':sequencecheck' => $this->get_question_attempt()->get_num_steps(),
+ );
+ foreach ($data as $name => $value) {
+ $fulldata[$prefix . $name] = $value;
+ }
+ $this->quba->process_all_actions(time(), $fulldata);
+ }
+
+ protected function process_autosave($data) {
+ $this->quba->process_autosave($this->slot, $data);
+ }
+
+ protected function finish() {
+ $this->quba->finish_all_questions();
}
protected function manual_grade($comment, $mark, $commentformat = null) {
$this->quba->manual_grade($this->slot, $comment, $mark, $commentformat);
}
+ protected function save_quba(moodle_database $db = null) {
+ question_engine::save_questions_usage_by_activity($this->quba, $db);
+ }
+
+ protected function load_quba(moodle_database $db = null) {
+ $this->quba = question_engine::load_questions_usage_by_activity($this->quba->get_id(), $db);
+ }
+
+ protected function delete_quba() {
+ question_engine::delete_questions_usage_by_activity($this->quba->get_id());
+ $this->quba = null;
+ }
+
protected function check_current_state($state) {
$this->assertEquals($state, $this->quba->get_question_state($this->slot),
'Questions is in the wrong state.');
@@ -684,6 +754,36 @@ protected function render() {
$this->currentoutput = $this->quba->render_question($this->slot, $this->displayoptions);
}
+ protected function check_output_contains_text_input($name, $value = null, $enabled = true) {
+ $attributes = array(
+ 'type' => 'text',
+ 'name' => $this->quba->get_field_prefix($this->slot) . $name,
+ );
+ if (!is_null($value)) {
+ $attributes['value'] = $value;
+ }
+ if (!$enabled) {
+ $attributes['readonly'] = 'readonly';
+ }
+ $matcher = $this->get_tag_matcher('input', $attributes);
+ $this->assertTag($matcher, $this->currentoutput,
+ 'Looking for an input with attributes ' . html_writer::attributes($attributes) . ' in ' . $this->currentoutput);
+
+ if ($enabled) {
+ $matcher['attributes']['readonly'] = 'readonly';
+ $this->assertNotTag($matcher, $this->currentoutput,
+ 'input with attributes ' . html_writer::attributes($attributes) .
+ ' should not be read-only in ' . $this->currentoutput);
+ }
+ }
+
+ protected function get_tag_matcher($tag, $attributes) {
+ return array(
+ 'tag' => $tag,
+ 'attributes' => $attributes,
+ );
+ }
+
/**
* @param $condition one or more Expectations. (users varargs).
*/
@@ -0,0 +1,143 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains tests for the question_attempt class.
+ *
+ * Action methods like start, process_action and finish are assumed to be
+ * tested by walkthrough tests in the various behaviours.
+ *
+ * @package moodlecore
+ * @subpackage questionengine
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once(dirname(__FILE__) . '/../lib.php');
+require_once(dirname(__FILE__) . '/helpers.php');
+
+
+/**
+ * Unit tests for loading data into the {@link question_attempt} class.
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_attempt_db_test extends data_loading_method_test_base {
+ public function test_load() {
+ $records = new question_test_recordset(array(
+ array('questionattemptid', 'contextid', 'questionusageid', 'slot',
+ 'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'flagged',
+ 'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
+ 'attemptstepid', 'sequencenumber', 'state', 'fraction',
+ 'timecreated', 'userid', 'name', 'value'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 2, 1, 'complete', null, 1256233705, 1, 'answer', '1'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 1, '', '', '', 1256233790, 3, 2, 'complete', null, 1256233710, 1, 'answer', '0'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 4, 3, 'complete', null, 1256233715, 1, 'answer', '1'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 5, 4, 'gradedright', 1.0000000, 1256233720, 1, '-finish', '1'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-comment', 'Not good enough!'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-mark', '1'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-maxmark', '2'),
+ ));
+
+ $question = test_question_maker::make_question('truefalse', 'true');
+ $question->id = -1;
+
+ question_bank::start_unit_test();
+ question_bank::load_test_question_data($question);
+ $qa = question_attempt::load_from_records($records, 1, new question_usage_null_observer(), 'deferredfeedback');
+ question_bank::end_unit_test();
+
+ $this->assertEquals($question->questiontext, $qa->get_question()->questiontext);
+
+ $this->assertEquals(6, $qa->get_num_steps());
+
+ $step = $qa->get_step(0);
+ $this->assertEquals(question_state::$todo, $step->get_state());
+ $this->assertNull($step->get_fraction());
+ $this->assertEquals(1256233700, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array(), $step->get_all_data());
+
+ $step = $qa->get_step(1);
+ $this->assertEquals(question_state::$complete, $step->get_state());
+ $this->assertNull($step->get_fraction());
+ $this->assertEquals(1256233705, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array('answer' => '1'), $step->get_all_data());
+
+ $step = $qa->get_step(2);
+ $this->assertEquals(question_state::$complete, $step->get_state());
+ $this->assertNull($step->get_fraction());
+ $this->assertEquals(1256233710, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array('answer' => '0'), $step->get_all_data());
+
+ $step = $qa->get_step(3);
+ $this->assertEquals(question_state::$complete, $step->get_state());
+ $this->assertNull($step->get_fraction());
+ $this->assertEquals(1256233715, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array('answer' => '1'), $step->get_all_data());
+
+ $step = $qa->get_step(4);
+ $this->assertEquals(question_state::$gradedright, $step->get_state());
+ $this->assertEquals(1, $step->get_fraction());
+ $this->assertEquals(1256233720, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array('-finish' => '1'), $step->get_all_data());
+
+ $step = $qa->get_step(5);
+ $this->assertEquals(question_state::$mangrpartial, $step->get_state());
+ $this->assertEquals(0.5, $step->get_fraction());
+ $this->assertEquals(1256233790, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array('-comment' => 'Not good enough!', '-mark' => '1', '-maxmark' => '2'),
+ $step->get_all_data());
+ }
+
+ public function test_load_missing_question() {
+ $records = new question_test_recordset(array(
+ array('questionattemptid', 'contextid', 'questionusageid', 'slot',
+ 'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'flagged',
+ 'questionsummary', 'rightanswer', 'responsesummary', 'timemodified',
+ 'attemptstepid', 'sequencenumber', 'state', 'fraction',
+ 'timecreated', 'userid', 'name', 'value'),
+ array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null),
+ ));
+
+ question_bank::start_unit_test();
+ $qa = question_attempt::load_from_records($records, 1, new question_usage_null_observer(), 'deferredfeedback');
+ question_bank::end_unit_test();
+
+ $missingq = question_bank::get_qtype('missingtype')->make_deleted_instance(-1, 2);
+ $this->assertEquals($missingq, $qa->get_question());
+
+ $this->assertEquals(1, $qa->get_num_steps());
+
+ $step = $qa->get_step(0);
+ $this->assertEquals(question_state::$todo, $step->get_state());
+ $this->assertNull($step->get_fraction());
+ $this->assertEquals(1256233700, $step->get_timecreated());
+ $this->assertEquals(1, $step->get_user_id());
+ $this->assertEquals(array(), $step->get_all_data());
+ }
+}
Oops, something went wrong.

0 comments on commit eca230b

Please sign in to comment.