<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Irajput</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Irajput"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Irajput"/>
	<updated>2026-07-21T05:55:33Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164530</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164530"/>
		<updated>2025-04-22T03:30:59Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test results.png|900px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|900px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines the approach taken to improve test coverage for the questionnaire_helper.rb file in Expertiza. Test cases were developed for all key methods, including adjust_advice_size, update_questionnaire_questions, and questionnaire_factory. With these additions, the file now has full method coverage. The focus going forward will be to maintain this coverage and ensure that any future changes to the module are accompanied by corresponding tests.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
* Demo Video:&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_results.png&amp;diff=164529</id>
		<title>File:Test results.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_results.png&amp;diff=164529"/>
		<updated>2025-04-22T03:30:39Z</updated>

		<summary type="html">&lt;p&gt;Irajput: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164528</id>
		<title>File:Test suite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164528"/>
		<updated>2025-04-22T03:29:02Z</updated>

		<summary type="html">&lt;p&gt;Irajput: Irajput reverted File:Test suite.png to an old version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164527</id>
		<title>File:Test suite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164527"/>
		<updated>2025-04-22T03:28:41Z</updated>

		<summary type="html">&lt;p&gt;Irajput: Irajput reverted File:Test suite.png to an old version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164525</id>
		<title>File:Test suite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164525"/>
		<updated>2025-04-22T03:26:46Z</updated>

		<summary type="html">&lt;p&gt;Irajput: Irajput uploaded a new version of File:Test suite.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164510</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164510"/>
		<updated>2025-04-22T02:50:49Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Project Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|900px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|900px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines the approach taken to improve test coverage for the questionnaire_helper.rb file in Expertiza. Test cases were developed for all key methods, including adjust_advice_size, update_questionnaire_questions, and questionnaire_factory. With these additions, the file now has full method coverage. The focus going forward will be to maintain this coverage and ensure that any future changes to the module are accompanied by corresponding tests.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
* Demo Video:&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164509</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164509"/>
		<updated>2025-04-22T02:50:14Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Conclusion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|900px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|900px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines the approach taken to improve test coverage for the questionnaire_helper.rb file in Expertiza. Test cases were developed for all key methods, including adjust_advice_size, update_questionnaire_questions, and questionnaire_factory. With these additions, the file now has full method coverage. The focus going forward will be to maintain this coverage and ensure that any future changes to the module are accompanied by corresponding tests.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
* Demo Video:&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164507</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164507"/>
		<updated>2025-04-22T02:47:57Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|900px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|900px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
* Demo Video:&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164505</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164505"/>
		<updated>2025-04-22T02:47:12Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
* Demo Video:&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164504</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164504"/>
		<updated>2025-04-22T02:46:51Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164503</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164503"/>
		<updated>2025-04-22T02:46:43Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164501</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164501"/>
		<updated>2025-04-22T02:46:26Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'updates all changed fields and saves the question' do&lt;br /&gt;
      # Ensures all changed fields are updated and saved.&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;txt&amp;quot;).and_return(&amp;quot;old&amp;quot;)&lt;br /&gt;
      allow(@question1).to receive(:send).with(&amp;quot;weight&amp;quot;).and_return(&amp;quot;1&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;txt=&amp;quot;, &amp;quot;new&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:send).with(&amp;quot;weight=&amp;quot;, &amp;quot;2&amp;quot;)&lt;br /&gt;
      expect(@question1).to receive(:save)&lt;br /&gt;
    &lt;br /&gt;
      allow(self).to receive(:params).and_return({&lt;br /&gt;
        question: {&lt;br /&gt;
          &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;new&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
      })&lt;br /&gt;
    &lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164500</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164500"/>
		<updated>2025-04-22T02:44:49Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164499</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164499"/>
		<updated>2025-04-22T02:44:29Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164498</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164498"/>
		<updated>2025-04-22T02:43:35Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164497</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164497"/>
		<updated>2025-04-22T02:43:27Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
'''Ruby code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot; line&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164496</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164496"/>
		<updated>2025-04-22T02:41:52Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
'''Ruby code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164495</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164495"/>
		<updated>2025-04-22T02:38:45Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'deletes advice entries outside valid score range' do&lt;br /&gt;
        # Verifies advice entries outside the valid score range are deleted.&lt;br /&gt;
        expect(QuestionAdvice).to receive(:delete).with(&lt;br /&gt;
          ['question_id = ? AND (score &amp;gt; ? OR score &amp;lt; ?)', question.id, 3, 1]&lt;br /&gt;
        )&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, question)&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164492</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164492"/>
		<updated>2025-04-22T02:37:29Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* questionnaire_factory */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns the correct questionnaire instance for a valid type' do&lt;br /&gt;
      # Ensures the correct questionnaire instance is returned for valid types.&lt;br /&gt;
      instance = questionnaire_factory('ReviewQuestionnaire')&lt;br /&gt;
      expect(instance).to be_a(ReviewQuestionnaire)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'sets flash error and returns nil for an invalid type' do&lt;br /&gt;
      # Verifies flash error is set and nil is returned for invalid types.&lt;br /&gt;
      flash_hash = {}&lt;br /&gt;
      allow(self).to receive(:flash).and_return(flash_hash)&lt;br /&gt;
&lt;br /&gt;
      result = questionnaire_factory('InvalidType')&lt;br /&gt;
      expect(result).to be_nil&lt;br /&gt;
      expect(flash_hash[:error]).to eq('Error: Undefined Questionnaire')&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164491</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164491"/>
		<updated>2025-04-22T02:35:40Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* update_questionnaire_questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164490</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164490"/>
		<updated>2025-04-22T02:35:15Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* update_questionnaire_questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
    before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164486</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164486"/>
		<updated>2025-04-22T02:34:30Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Expectations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
before do&lt;br /&gt;
      extend QuestionnaireHelper&lt;br /&gt;
&lt;br /&gt;
      @question1 = double('Question', id: 1)&lt;br /&gt;
      @question2 = double('Question', id: 2)&lt;br /&gt;
&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;1&amp;quot;).and_return(@question1)&lt;br /&gt;
      allow(Question).to receive(:find).with(&amp;quot;2&amp;quot;).and_return(@question2)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns early if params[:question] is nil' do&lt;br /&gt;
      # Ensures method exits early when no questions are provided.&lt;br /&gt;
      allow(self).to receive(:params).and_return({})&lt;br /&gt;
      expect(@question1).not_to receive(:save)&lt;br /&gt;
      update_questionnaire_questions&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164482</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164482"/>
		<updated>2025-04-22T02:33:26Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Code Snippet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164480</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164480"/>
		<updated>2025-04-22T02:32:30Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Expectations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
==== Code Snippet ====&lt;br /&gt;
&lt;br /&gt;
  let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
    context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not perform any operations' do&lt;br /&gt;
        # Ensures no operations are performed for non-scored questions.&lt;br /&gt;
        non_scored_question = double('Question')&lt;br /&gt;
        expect(non_scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        expect(QuestionAdvice).not_to receive(:delete)&lt;br /&gt;
        QuestionnaireHelper.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164479</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164479"/>
		<updated>2025-04-22T02:31:56Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164478</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164478"/>
		<updated>2025-04-22T02:31:18Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
* Code snippet&lt;br /&gt;
    let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164477</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164477"/>
		<updated>2025-04-22T02:30:58Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
    let(:questionnaire) { double('Questionnaire', min_question_score: 1, max_question_score: 3) }&lt;br /&gt;
    let(:question) { double('ScoredQuestion', id: 101, question_advices: []) }&lt;br /&gt;
&lt;br /&gt;
    before do&lt;br /&gt;
      allow(question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164474</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164474"/>
		<updated>2025-04-22T02:26:44Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|800px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164473</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164473"/>
		<updated>2025-04-22T02:26:32Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|750px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164471</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164471"/>
		<updated>2025-04-22T02:26:23Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png|500px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164470</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164470"/>
		<updated>2025-04-22T02:26:09Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:Test_suite.png]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164469</id>
		<title>File:Test suite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_suite.png&amp;diff=164469"/>
		<updated>2025-04-22T02:25:37Z</updated>

		<summary type="html">&lt;p&gt;Irajput: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164468</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164468"/>
		<updated>2025-04-22T02:23:37Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:number_of_cases.png|500px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164467</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164467"/>
		<updated>2025-04-22T02:23:20Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:number_of_cases.png|500px]] &amp;lt;br&amp;gt;&lt;br /&gt;
[[File:lines_covered.jpg|500px]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164466</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164466"/>
		<updated>2025-04-22T02:23:07Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:number_of_cases.png|500px]]&lt;br /&gt;
[[File:lines_covered.jpg|500px]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:new_code.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164465</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=164465"/>
		<updated>2025-04-22T02:22:52Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 42.86%&lt;br /&gt;
* Current coverage: 100.00%&lt;br /&gt;
[[File:number_of_cases.png|500px]]&lt;br /&gt;
[[File:lines_covered.jpg|500px]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:new_code.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza Github]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163850</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163850"/>
		<updated>2025-04-08T03:21:14Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: TBD after implementation&lt;br /&gt;
* Current coverage: TBD after implementation&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* GitHub Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
* GitHub Repository: [https://github.com/ishani-rajput/expertiza]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163849</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163849"/>
		<updated>2025-04-08T03:20:11Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Current Code Coverage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2529. Testing for the Questionnaire Helper in Expertiza&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project focuses on developing test cases for the questionnaire_helper module within Expertiza, an open-source assignment and project management system built using Ruby on Rails. The platform supports collaborative learning by enabling instructors to design assignments, set up topics for sign-up, and organize student teams. Meanwhile, students can choose topics, work in groups, and engage in peer assessments to support each other's learning. The primary aim of this project is to create thorough test plans and improve code coverage for these helper modules, ensuring their functionality and dependability within the Expertiza system.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi (pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Dependency Injection Principle (DIP)&lt;br /&gt;
The QuestionnaireHelper methods accept objects like questionnaire or scored_question as parameters rather than instantiating them internally. This reflects the Dependency Injection Principle, promoting loose coupling. It also makes the methods easier to test and reuse, since dependencies are provided externally and can be mocked or substituted as needed.&lt;br /&gt;
&lt;br /&gt;
* Single Responsibility Principle (SRP)&lt;br /&gt;
Each method in the QuestionnaireHelper module demonstrates the Single Responsibility Principle by handling a specific, well-defined task. For instance, the adjust_advice_size method is solely focused on resizing the advice fields based on score ranges, while the questionnaire_factory method exclusively deals with instantiating questionnaire objects depending on their type. This separation of concerns enhances both the clarity and maintainability of the code.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP)&lt;br /&gt;
Although not immediately visible in limited code snippets, the design follows the Open/Closed Principle by being open to extension but closed to modification. For example, to support new questionnaire types, developers can extend the logic in questionnaire_factory without altering the existing structure, making the system more scalable and adaptable to future changes.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern&lt;br /&gt;
The questionnaire_factory method resembles the Factory Method Pattern by generating different types of questionnaire instances based on input parameters. This pattern enables the dynamic creation of objects without hard-coding their specific classes, improving flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
* Strategy Pattern&lt;br /&gt;
Another applicable pattern is the Strategy Pattern, especially relevant if the behavior of questionnaires (e.g., scoring, rendering, or validation) varies by type. If such behaviors are encapsulated in interchangeable classes and selected at runtime (possibly via the factory method), it aligns well with the Strategy Pattern. This promotes code reusability and simplifies the addition of new behavior types without impacting existing logic.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The adjust_advice_size method ensures that every ScoredQuestion has exactly one QuestionAdvice for each score in the range defined by its parent questionnaire. It removes advice entries outside the valid range and eliminates duplicates, while also creating missing entries for scores within range.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Create a mock or real Questionnaire instance with:&lt;br /&gt;
  min_question_score = 1&lt;br /&gt;
  max_question_score = 5&lt;br /&gt;
&lt;br /&gt;
* Create a ScoredQuestion with:&lt;br /&gt;
  id assigned&lt;br /&gt;
  question_advices association&lt;br /&gt;
&lt;br /&gt;
* Populate the QuestionAdvice table with:&lt;br /&gt;
  Advice entries within the valid range&lt;br /&gt;
  Advice entries outside the valid range&lt;br /&gt;
  Duplicate advice entries for the same score&lt;br /&gt;
&lt;br /&gt;
* Stub or use an in-memory DB to test without affecting real data.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: question is not a ScoredQuestion&lt;br /&gt;
  Should perform no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: question is a ScoredQuestion, with:&lt;br /&gt;
  Valid advice range entries → should remain untouched.&lt;br /&gt;
  Advice with scores outside [min, max] → should be deleted.&lt;br /&gt;
  Duplicate advice entries → should be reduced to one.&lt;br /&gt;
  Missing advice entries within valid range → should be created.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, the method should exit early and make no database changes.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Only one QuestionAdvice should exist for each score in [min, max].&lt;br /&gt;
  No QuestionAdvice should exist with scores outside [min, max].&lt;br /&gt;
  All newly created QuestionAdvice entries should have the correct score and question_id.&lt;br /&gt;
  The total number of QuestionAdvice entries for the question should equal (max - min + 1).&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The questionnaire_factory method is responsible for dynamically creating a new instance of a questionnaire based on a type string provided as input. It uses the QUESTIONNAIRE_MAP constant to resolve the correct class. If the type is not recognized, it sets an error message in the flash hash and returns nil.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
* Ensure QUESTIONNAIRE_MAP is correctly defined with mappings like:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;ReviewQuestionnaire&amp;quot; =&amp;gt; ReviewQuestionnaire,&lt;br /&gt;
  &amp;quot;SurveyQuestionnaire&amp;quot; =&amp;gt; SurveyQuestionnaire&lt;br /&gt;
  }&lt;br /&gt;
* Stub or create minimal versions of the questionnaire classes (ReviewQuestionnaire, etc.) that support .new.&lt;br /&gt;
* Mock the flash hash to observe changes when the type is invalid.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Context 1: Valid questionnaire type is provided (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;).&lt;br /&gt;
  Should return a new instance of ReviewQuestionnaire.&lt;br /&gt;
&lt;br /&gt;
* Context 2: Invalid questionnaire type is provided (e.g., &amp;quot;InvalidType&amp;quot;).&lt;br /&gt;
  Should set an error message in flash[:error].&lt;br /&gt;
  Should return nil.&lt;br /&gt;
&lt;br /&gt;
* Context 3: type is nil or an empty string.&lt;br /&gt;
  Should be treated as an invalid type.&lt;br /&gt;
  Should set flash error and return nil.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1:&lt;br /&gt;
  A new instance of the correct class is returned.&lt;br /&gt;
  No flash error is set.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  flash[:error] is set to 'Error: Undefined Questionnaire'.&lt;br /&gt;
  The method returns nil.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Same expectations as Context 2 (error set, return nil).&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The update_questionnaire_questions method updates existing Question records with new attribute values received from the request parameters. It ensures that only the changed attributes are updated and saved, thereby optimizing database writes.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
* Define params[:question] as a hash with:&lt;br /&gt;
  {&lt;br /&gt;
  &amp;quot;1&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Updated question text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;2&amp;quot; },&lt;br /&gt;
  &amp;quot;2&amp;quot; =&amp;gt; { &amp;quot;txt&amp;quot; =&amp;gt; &amp;quot;Another text&amp;quot;, &amp;quot;weight&amp;quot; =&amp;gt; &amp;quot;1&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* Create corresponding Question objects with:&lt;br /&gt;
  IDs 1 and 2&lt;br /&gt;
  Initial attributes set to different values or the same (for testing updates vs no-ops)&lt;br /&gt;
&lt;br /&gt;
* Stub or set up the params object inside a test controller or context.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
* Context 1: params[:question] is nil&lt;br /&gt;
   Method should return immediately and make no changes.&lt;br /&gt;
&lt;br /&gt;
* Context 2: params[:question] contains valid data&lt;br /&gt;
  Some attributes differ from the existing Question values → should update and save.&lt;br /&gt;
  Some attributes match existing values → should not update those fields.&lt;br /&gt;
&lt;br /&gt;
* Context 3: Mixed changes&lt;br /&gt;
  One Question has all new values → should be updated and saved.&lt;br /&gt;
  One Question has no changed values → should not call save.&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
* In Context 1, no Question records should be loaded or updated.&lt;br /&gt;
* In Context 2:&lt;br /&gt;
  Each Question should receive updated values only for attributes that differ.&lt;br /&gt;
  question.save should be called only if at least one attribute is updated.&lt;br /&gt;
* In Context 3:&lt;br /&gt;
  Only Question objects with modified attributes should trigger a .save call.&lt;br /&gt;
  Attributes already matching current values should not be re-assigned.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: TBD after implementation&lt;br /&gt;
* Current coverage: TBD after implementation&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document presents a comprehensive strategy to improve testing and code coverage for the questionnaire_helper files in Expertiza. With clear objectives such as creating detailed test plans and scenarios, the project seeks to close existing coverage gaps. While the questionnaire_helper.rb file showed only slight improvements—mainly because the update_questionnaire_questions method was already fully covered—the focus now shifts to executing the proposed test plans to ensure robust testing and reliability across all key functionalities in both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163662</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163662"/>
		<updated>2025-04-08T02:03:05Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* update_questionnaire_questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163660</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163660"/>
		<updated>2025-04-08T02:02:34Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Expectations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163659</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163659"/>
		<updated>2025-04-08T02:02:27Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Contexts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163658</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163658"/>
		<updated>2025-04-08T02:02:18Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163657</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163657"/>
		<updated>2025-04-08T02:02:10Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Objective */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163656</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163656"/>
		<updated>2025-04-08T02:01:55Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Expectations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163654</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163654"/>
		<updated>2025-04-08T02:01:38Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Contexts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163652</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163652"/>
		<updated>2025-04-08T02:01:27Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Test Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ====&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Selected Topics are Present: This context tests the behavior of the method when there are selected topics. It sets up a selected topic that is not waitlisted and expects the generated HTML to include a table row with a yellow background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;yellow&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topic is Waitlisted: This context tests the behavior when the selected topic is waitlisted. It sets up a selected topic that is waitlisted and expects the generated HTML to include a table row with a light gray background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Not Present: This context checks what happens when there are no selected topics. It mocks a method (get_topic_bg_color_review_bids) to return a specific background color and expects the generated HTML to include a table row with that background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are not present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')&lt;br /&gt;
        selected_topics = []&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(255,255,255)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Nil: This context tests how the method handles cases where the selected topics parameter is nil. It expects the generated HTML to include a table row with a specific background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are nil' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topics = nil&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(47,352,0)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163651</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163651"/>
		<updated>2025-04-08T02:01:18Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Objective */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2025 E2440. Testing for questionnaire_helper.&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Aniruddha Rajenkar (aarajenkar@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Priya Gandhi(pgandhi4@ncsu.edu)&lt;br /&gt;
* Saisumanth Tallapragada (stallap@ncsu.edu)&lt;br /&gt;
* Ishani Rajput (irajput@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
In Expertiza, the QuestionnaireHelper module plays a key role in managing questionnaires by providing methods to handle advice sizing, question updates, and questionnaire creation based on type. It also contains constants that assist in executing these tasks. These methods contribute to the smooth handling of questionnaire functionalities across the application. Below are the constants and methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a selected_topic, the number of participants (num_participants), and a review_bid.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system. For example, selected_topic is set with a topic ID of 1 and is not waitlisted initially.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Selected Topics are Present: This context tests the behavior of the method when there are selected topics. It sets up a selected topic that is not waitlisted and expects the generated HTML to include a table row with a yellow background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;yellow&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topic is Waitlisted: This context tests the behavior when the selected topic is waitlisted. It sets up a selected topic that is waitlisted and expects the generated HTML to include a table row with a light gray background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Not Present: This context checks what happens when there are no selected topics. It mocks a method (get_topic_bg_color_review_bids) to return a specific background color and expects the generated HTML to include a table row with that background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are not present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')&lt;br /&gt;
        selected_topics = []&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(255,255,255)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Nil: This context tests how the method handles cases where the selected topics parameter is nil. It expects the generated HTML to include a table row with a specific background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are nil' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topics = nil&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(47,352,0)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
* Github Pull Request: [https://github.com/expertiza/expertiza/pull/2950 Pull Request]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163632</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163632"/>
		<updated>2025-04-08T01:48:39Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Objective 1: Develop code testing scenarios for questionnaire_helper */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2024 E2440. Testing for questionnaire_helper, review_bids_helper&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Muhammet Mustafa Olmez (molmez@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Neha Vijay Patil (npatil2@ncsu.edu)&lt;br /&gt;
* Prachit Mhalgi (psmhalgi@ncsu.edu)&lt;br /&gt;
* Sahil Santosh Sawant (ssawant2@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
The QuestionnaireHelper module contains several methods to assist with managing questionnaires in expertiza. QuestionnaireHelper provides methods for adjusting advice size, updating questionnaire questions, and creating questionnaire instances based on types. It also defines constants to facilitate these functionalities. These methods are likely used within the application to handle questionnaire-related tasks efficiently. Let's break down the class and its methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective : Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
== Objective 2: Develop code testing scenarios for review_bids_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the ReviewBidsHelper class seems to adhere to the SRP. For example, get_intelligent_topic_row_review_bids is responsible for generating HTML code for topic rows, while get_topic_bg_color_review_bids determines the background color for a topic. This adherence ensures that each method has a clear and distinct purpose, enhancing maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
Although not explicitly labeled as a factory method, get_intelligent_topic_row_review_bids can be interpreted as following a similar pattern. It dynamically generates HTML code based on different scenarios, akin to a factory producing instances of objects. This promotes flexibility and extensibility in generating HTML representations of topics.&lt;br /&gt;
&lt;br /&gt;
After reviewing the ReviewBidsHelper module, we identified two methods that require testing:&lt;br /&gt;
&lt;br /&gt;
=== get_intelligent_topic_row_review_bids ===&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_intelligent_topic_row_review_bids correctly renders the topic row for the topics table in review_bids/show.html.erb. The #get_intelligent_topic_row_review_bids method is being described. This method likely generates HTML code for displaying topic rows based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a selected_topic, the number of participants (num_participants), and a review_bid.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system. For example, selected_topic is set with a topic ID of 1 and is not waitlisted initially.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Selected Topics are Present: This context tests the behavior of the method when there are selected topics. It sets up a selected topic that is not waitlisted and expects the generated HTML to include a table row with a yellow background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;yellow&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topic is Waitlisted: This context tests the behavior when the selected topic is waitlisted. It sets up a selected topic that is waitlisted and expects the generated HTML to include a table row with a light gray background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Not Present: This context checks what happens when there are no selected topics. It mocks a method (get_topic_bg_color_review_bids) to return a specific background color and expects the generated HTML to include a table row with that background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are not present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')&lt;br /&gt;
        selected_topics = []&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(255,255,255)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Nil: This context tests how the method handles cases where the selected topics parameter is nil. It expects the generated HTML to include a table row with a specific background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are nil' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topics = nil&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(47,352,0)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== get_topic_bg_color_review_bids ===&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
&lt;br /&gt;
* Link to Testing video: [https://youtu.be/jDfmPUgDDXA]&lt;br /&gt;
&lt;br /&gt;
* Link to pull request : [https://github.com/expertiza/expertiza/pull/2799 here]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;br /&gt;
3. Pluggable reputation systems for peer review: A web-service approach (https://doi.org/10.1109/FIE.2015.7344292)&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163631</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163631"/>
		<updated>2025-04-08T01:48:28Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Constants */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2024 E2440. Testing for questionnaire_helper, review_bids_helper&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Muhammet Mustafa Olmez (molmez@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Neha Vijay Patil (npatil2@ncsu.edu)&lt;br /&gt;
* Prachit Mhalgi (psmhalgi@ncsu.edu)&lt;br /&gt;
* Sahil Santosh Sawant (ssawant2@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
The QuestionnaireHelper module contains several methods to assist with managing questionnaires in expertiza. QuestionnaireHelper provides methods for adjusting advice size, updating questionnaire questions, and creating questionnaire instances based on types. It also defines constants to facilitate these functionalities. These methods are likely used within the application to handle questionnaire-related tasks efficiently. Let's break down the class and its methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants serve as index references for different fields in a CSV row related to questionnaire data, such as the question text, type, parameters, and weight.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - A constant hash that maps string identifiers of questionnaire types to their respective Ruby class names.&lt;br /&gt;
   - Enables dynamic creation of questionnaire objects based on type strings.&lt;br /&gt;
   - Used by the questionnaire_factory method to instantiate the appropriate questionnaire class.&lt;br /&gt;
   - Helps avoid repetitive conditional logic by centralizing type-to-class associations.&lt;br /&gt;
   - Example mapping: 'ReviewQuestionnaire' =&amp;gt; ReviewQuestionnaire&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective 1: Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
== Objective 2: Develop code testing scenarios for review_bids_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the ReviewBidsHelper class seems to adhere to the SRP. For example, get_intelligent_topic_row_review_bids is responsible for generating HTML code for topic rows, while get_topic_bg_color_review_bids determines the background color for a topic. This adherence ensures that each method has a clear and distinct purpose, enhancing maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
Although not explicitly labeled as a factory method, get_intelligent_topic_row_review_bids can be interpreted as following a similar pattern. It dynamically generates HTML code based on different scenarios, akin to a factory producing instances of objects. This promotes flexibility and extensibility in generating HTML representations of topics.&lt;br /&gt;
&lt;br /&gt;
After reviewing the ReviewBidsHelper module, we identified two methods that require testing:&lt;br /&gt;
&lt;br /&gt;
=== get_intelligent_topic_row_review_bids ===&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_intelligent_topic_row_review_bids correctly renders the topic row for the topics table in review_bids/show.html.erb. The #get_intelligent_topic_row_review_bids method is being described. This method likely generates HTML code for displaying topic rows based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a selected_topic, the number of participants (num_participants), and a review_bid.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system. For example, selected_topic is set with a topic ID of 1 and is not waitlisted initially.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Selected Topics are Present: This context tests the behavior of the method when there are selected topics. It sets up a selected topic that is not waitlisted and expects the generated HTML to include a table row with a yellow background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;yellow&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topic is Waitlisted: This context tests the behavior when the selected topic is waitlisted. It sets up a selected topic that is waitlisted and expects the generated HTML to include a table row with a light gray background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Not Present: This context checks what happens when there are no selected topics. It mocks a method (get_topic_bg_color_review_bids) to return a specific background color and expects the generated HTML to include a table row with that background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are not present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')&lt;br /&gt;
        selected_topics = []&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(255,255,255)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Nil: This context tests how the method handles cases where the selected topics parameter is nil. It expects the generated HTML to include a table row with a specific background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are nil' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topics = nil&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(47,352,0)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== get_topic_bg_color_review_bids ===&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
&lt;br /&gt;
* Link to Testing video: [https://youtu.be/jDfmPUgDDXA]&lt;br /&gt;
&lt;br /&gt;
* Link to pull request : [https://github.com/expertiza/expertiza/pull/2799 here]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;br /&gt;
3. Pluggable reputation systems for peer review: A web-service approach (https://doi.org/10.1109/FIE.2015.7344292)&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163623</id>
		<title>CSC/ECE 517 Spring 2025 - E2529. Testing for the Questionnaire Helper in Expertiza</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2529._Testing_for_the_Questionnaire_Helper_in_Expertiza&amp;diff=163623"/>
		<updated>2025-04-08T01:43:31Z</updated>

		<summary type="html">&lt;p&gt;Irajput: /* Methods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the changes made for the Spring 2024 E2440. Testing for questionnaire_helper, review_bids_helper&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
Our project involves writing test cases for the `questionnaire_helper` and `review_bids_helper` files in Expertiza, an open-source assignment/project management portal built on the Ruby on Rails framework. This platform facilitates collaborative learning and feedback among both instructors and students. Instructors can create and customize assignments, define topics for student sign-ups, and manage teams for various projects. Students, on the other hand, can sign up for topics, form teams, and participate in peer reviews to enhance each other's learning experiences. Our goal is to develop comprehensive test plans and increase code coverage for these helper files to ensure their reliability and effectiveness in the Expertiza platform.&lt;br /&gt;
&lt;br /&gt;
=== Current Code Coverage ===&lt;br /&gt;
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
* Develop test plans/scenarios for questionnaire_helper.rb&lt;br /&gt;
* Improve code coverage for questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Files Involved ===&lt;br /&gt;
&lt;br /&gt;
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb&lt;br /&gt;
&lt;br /&gt;
=== Mentor ===&lt;br /&gt;
&lt;br /&gt;
* Muhammet Mustafa Olmez (molmez@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
=== Team Members ===&lt;br /&gt;
&lt;br /&gt;
* Neha Vijay Patil (npatil2@ncsu.edu)&lt;br /&gt;
* Prachit Mhalgi (psmhalgi@ncsu.edu)&lt;br /&gt;
* Sahil Santosh Sawant (ssawant2@ncsu.edu)&lt;br /&gt;
&lt;br /&gt;
== Class and Method Overview ==&lt;br /&gt;
&lt;br /&gt;
=== QuestionnaireHelper ===&lt;br /&gt;
&lt;br /&gt;
The QuestionnaireHelper module contains several methods to assist with managing questionnaires in expertiza. QuestionnaireHelper provides methods for adjusting advice size, updating questionnaire questions, and creating questionnaire instances based on types. It also defines constants to facilitate these functionalities. These methods are likely used within the application to handle questionnaire-related tasks efficiently. Let's break down the class and its methods:&lt;br /&gt;
&lt;br /&gt;
==== Constants ====&lt;br /&gt;
&lt;br /&gt;
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT&lt;br /&gt;
   - These constants define indices for specific columns in a CSV file.&lt;br /&gt;
&lt;br /&gt;
QUESTIONNAIRE_MAP&lt;br /&gt;
   - This constant is a hash that maps questionnaire types to their respective questionnaire classes.&lt;br /&gt;
   - It's used by the `questionnaire_factory` method to determine the appropriate class to instantiate.&lt;br /&gt;
&lt;br /&gt;
==== Methods ====&lt;br /&gt;
&lt;br /&gt;
1. adjust_advice_size(questionnaire, question)&lt;br /&gt;
   - Ensures that for each valid score in a ScoredQuestion, there exists exactly one QuestionAdvice. It removes extra or invalid advice entries and adds missing ones.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `questionnaire`: An instance of a questionnaire that contains scoring boundaries (min_question_score and max_question_score)&lt;br /&gt;
     - `question`: An instance of ScoredQuestion for which advice needs to be validated and adjusted.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Checks if the question is a ScoredQuestion.&lt;br /&gt;
     - Retrieves the min and max possible scores for the questionnaire.&lt;br /&gt;
     - Deletes any QuestionAdvice entries for this question that are outside the score bounds.&lt;br /&gt;
     - Iterates through all valid scores in the range [min, max]:&lt;br /&gt;
       - Adds a new QuestionAdvice if none exists for that score.&lt;br /&gt;
       - Deletes extra advice entries if more than one exists for the same score.&lt;br /&gt;
   &lt;br /&gt;
2. update_questionnaire_questions&lt;br /&gt;
   - Updates the attributes of questions in a questionnaire based on form parameters. It only updates attributes whose values have actually changed.&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Returns early if params[:question] is nil.&lt;br /&gt;
     - Iterates through each question ID (k) and corresponding attributes (v) in params[:question].&lt;br /&gt;
     - Finds the Question object by ID.&lt;br /&gt;
     - For each attribute:&lt;br /&gt;
       - If the current value differs from the new value, updates the attribute. &lt;br /&gt;
     - Saves the updated question to the database.&lt;br /&gt;
&lt;br /&gt;
3. questionnaire_factory(type)&lt;br /&gt;
   - Creates a new instance of a questionnaire based on its type string using a predefined mapping.&lt;br /&gt;
   - Parameters:&lt;br /&gt;
     - `type`: A String representing the name of the questionnaire type (e.g., &amp;quot;ReviewQuestionnaire&amp;quot;)&lt;br /&gt;
   - Functionality:&lt;br /&gt;
     - Looks up the class constant from the QUESTIONNAIRE_MAP using the given type string.&lt;br /&gt;
     - If no match is found, sets a flash error (flash[:error]).&lt;br /&gt;
     - If a match is found, returns a new instance of the corresponding questionnaire class.&lt;br /&gt;
&lt;br /&gt;
== Objective 1: Develop code testing scenarios for questionnaire_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the QuestionnaireHelper class appears to adhere to the SRP by focusing on a single task or responsibility. For example, the adjust_advice_size method is responsible for adjusting the size of advice based on questionnaire scores, while the questionnaire_factory method is responsible for creating instances of questionnaire types based on the given type parameter. This adherence ensures that each method has a clear and distinct purpose, promoting code maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Open/Closed Principle (OCP):&lt;br /&gt;
While not explicitly evident in the provided snippets, the design allows for extension without modification, which aligns with the OCP. For instance, new types of questionnaires can be added without altering existing code by simply extending the questionnaire_factory method to accommodate the new types.&lt;br /&gt;
&lt;br /&gt;
* Dependency Injection Principle (DIP):&lt;br /&gt;
The methods in the QuestionnaireHelper class accept various objects (e.g., questionnaire, scored_question) as parameters, adhering to the principle of dependency injection. By accepting dependencies from external sources rather than creating them internally, these methods become more flexible and easily testable.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
The questionnaire_factory method can be seen as exhibiting characteristics of the factory method pattern. It dynamically creates instances of different questionnaire types based on the given type parameter, promoting flexibility and extensibility.&lt;br /&gt;
&lt;br /&gt;
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:&lt;br /&gt;
&lt;br /&gt;
* adjust_advice_size&lt;br /&gt;
* questionnaire_factory&lt;br /&gt;
* update_questionnaire_questions&lt;br /&gt;
&lt;br /&gt;
=== adjust_advice_size ===&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Question is a ScoredQuestion: This context tests the behavior when the question is a scored question. It sets up expectations related to the adjustment of advice size based on the questionnaire scores.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when question is a ScoredQuestion' do&lt;br /&gt;
      it 'adjusts advice size based on questionnaire scores' do&lt;br /&gt;
    allow(QuestionAdvice).to receive(:where).and_return([])&lt;br /&gt;
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))&lt;br /&gt;
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)&lt;br /&gt;
    described_class.adjust_advice_size(questionnaire, scored_question)&lt;br /&gt;
    expect(QuestionAdvice).to have_received(:where).exactly(10).times&lt;br /&gt;
    expect(scored_question.question_advices.size).to eq(10)&lt;br /&gt;
  end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Question is Not a ScoredQuestion: This context tests the behavior when the question is not a scored question. It verifies that in this case, the advice size is not adjusted.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when question is not a ScoredQuestion' do&lt;br /&gt;
      it 'does not adjust advice size' do&lt;br /&gt;
        allow(QuestionAdvice).to receive(:where)&lt;br /&gt;
        allow(QuestionAdvice).to receive(:new)&lt;br /&gt;
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)&lt;br /&gt;
        described_class.adjust_advice_size(questionnaire, non_scored_question)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:where)&lt;br /&gt;
        expect(QuestionAdvice).not_to have_received(:new)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use allow and expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== questionnaire_factory ===&lt;br /&gt;
&lt;br /&gt;
==== Method Description ====&lt;br /&gt;
The .questionnaire_factory method is described. It seems to be a factory method responsible for creating instances of different questionnaire types based on the given type parameter.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Given a Valid Type: This context verifies the behavior when a valid questionnaire type is provided. It expects that calling questionnaire_factory with a valid type results in an instance of the specified questionnaire type (ReviewQuestionnaire).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when given a valid type' do&lt;br /&gt;
      it 'returns an instance of the specified questionnaire type' do&lt;br /&gt;
        questionnaire_type = 'ReviewQuestionnaire'&lt;br /&gt;
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Given an Invalid Type: This context tests how the method handles an invalid questionnaire type. It expects that calling questionnaire_factory with an invalid type sets an error flash message indicating that the questionnaire type is undefined.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when given an invalid type' do&lt;br /&gt;
      it 'sets an error flash message' do&lt;br /&gt;
        questionnaire_type = 'UnknownQuestionnaire'&lt;br /&gt;
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect and change statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
=== update_questionnaire_questions ===&lt;br /&gt;
We did not implement test cases for this method because it was already covered in last semester by previous team.&lt;br /&gt;
&lt;br /&gt;
== Objective 2: Develop code testing scenarios for review_bids_helper ==&lt;br /&gt;
* Single Responsibility Principle (SRP):&lt;br /&gt;
Each method within the ReviewBidsHelper class seems to adhere to the SRP. For example, get_intelligent_topic_row_review_bids is responsible for generating HTML code for topic rows, while get_topic_bg_color_review_bids determines the background color for a topic. This adherence ensures that each method has a clear and distinct purpose, enhancing maintainability and readability.&lt;br /&gt;
&lt;br /&gt;
* Factory Method Pattern:&lt;br /&gt;
Although not explicitly labeled as a factory method, get_intelligent_topic_row_review_bids can be interpreted as following a similar pattern. It dynamically generates HTML code based on different scenarios, akin to a factory producing instances of objects. This promotes flexibility and extensibility in generating HTML representations of topics.&lt;br /&gt;
&lt;br /&gt;
After reviewing the ReviewBidsHelper module, we identified two methods that require testing:&lt;br /&gt;
&lt;br /&gt;
=== get_intelligent_topic_row_review_bids ===&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_intelligent_topic_row_review_bids correctly renders the topic row for the topics table in review_bids/show.html.erb. The #get_intelligent_topic_row_review_bids method is being described. This method likely generates HTML code for displaying topic rows based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a selected_topic, the number of participants (num_participants), and a review_bid.&lt;br /&gt;
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system. For example, selected_topic is set with a topic ID of 1 and is not waitlisted initially.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* When Selected Topics are Present: This context tests the behavior of the method when there are selected topics. It sets up a selected topic that is not waitlisted and expects the generated HTML to include a table row with a yellow background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;yellow&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topic is Waitlisted: This context tests the behavior when the selected topic is waitlisted. It sets up a selected topic that is waitlisted and expects the generated HTML to include a table row with a light gray background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Not Present: This context checks what happens when there are no selected topics. It mocks a method (get_topic_bg_color_review_bids) to return a specific background color and expects the generated HTML to include a table row with that background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are not present' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')&lt;br /&gt;
        selected_topics = []&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(255,255,255)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When Selected Topics are Nil: This context tests how the method handles cases where the selected topics parameter is nil. It expects the generated HTML to include a table row with a specific background color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topics are nil' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topics = nil&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr id=&amp;quot;topic_1&amp;quot; style=&amp;quot;background-color:rgb(47,352,0)&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each context contains an it block with an expectation. These expectations use expect statements to verify that the HTML generated by the method meets certain criteria, such as containing specific table row elements with appropriate background colors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== get_topic_bg_color_review_bids ===&lt;br /&gt;
&lt;br /&gt;
==== Objective ====&lt;br /&gt;
Verify that the method get_topic_bg_color_review_bids correctly calculates the background color for a topic in the review_bids/show.html.erb template. The #get_topic_bg_color_review_bids method is being described. This method likely determines the background color for a topic based on certain conditions.&lt;br /&gt;
&lt;br /&gt;
==== Test Setup ==== &lt;br /&gt;
Mock objects are set up using let statements. These include a topic, a review_bid, and the number of participants (num_participants). Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.&lt;br /&gt;
&lt;br /&gt;
==== Test Contexts ====&lt;br /&gt;
&lt;br /&gt;
* Returns RGB Color Code: This test checks if the method returns an RGB color code for the topic background color. It sets up an expectation that the returned color code matches the pattern rgb(\d+,\d+,\d+), indicating it's in the correct format.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 context 'when there are no review bids' do&lt;br /&gt;
      it 'returns default RGB color code' do&lt;br /&gt;
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* When There Are No Review Bids: This context tests the behavior when there are no review bids associated with the topic. It sets up an expectation that the method returns a default RGB color code (rgb(47,352,0)), likely indicating a green color.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context 'when selected topic is waitlisted' do&lt;br /&gt;
      it 'returns HTML code for topic row with appropriate background color' do&lt;br /&gt;
        selected_topic = instance_double('SelectedTopic')&lt;br /&gt;
        allow(selected_topic).to receive(:topic_id).and_return(1)&lt;br /&gt;
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)&lt;br /&gt;
        selected_topics = [selected_topic]&lt;br /&gt;
&lt;br /&gt;
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('&amp;lt;tr bgcolor=&amp;quot;lightgray&amp;quot;&amp;gt;')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Expectations ====&lt;br /&gt;
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.&lt;br /&gt;
&lt;br /&gt;
== Coverage Results ==&lt;br /&gt;
=== Questionnaire_helper ===&lt;br /&gt;
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb&lt;br /&gt;
* Current coverage:&lt;br /&gt;
&lt;br /&gt;
[[File:Questionnaire helper spec coverage.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Conversely, the questionnaire_helper.rb file saw marginal improvements, primarily due to existing full coverage in the update_questionnaire_questions method. Moving forward, the project will focus on implementing the outlined test plans, ensuring comprehensive testing and reliability for critical functionality across both helper files.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]&lt;br /&gt;
&lt;br /&gt;
* Link to Testing video: [https://youtu.be/jDfmPUgDDXA]&lt;br /&gt;
&lt;br /&gt;
* Link to pull request : [https://github.com/expertiza/expertiza/pull/2799 here]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
1. Expertiza on GitHub (https://github.com/expertiza/expertiza) &amp;lt;br&amp;gt;&lt;br /&gt;
2. The live Expertiza website (http://expertiza.ncsu.edu/) &amp;lt;br&amp;gt;&lt;br /&gt;
3. Pluggable reputation systems for peer review: A web-service approach (https://doi.org/10.1109/FIE.2015.7344292)&lt;/div&gt;</summary>
		<author><name>Irajput</name></author>
	</entry>
</feed>