CSC/ECE 517 Spring 2024 - E2440 Testing for questionnaire helper, review bids helper: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(26 intermediate revisions by the same user not shown)
Line 4: Line 4:


=== Problem Statement ===
=== Problem Statement ===
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.


 
=== Current Code Coverage ===
* Questionnaire_helper: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb
* Review_bids_helper: 17.65%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Freview_bids_helper.rb


=== Objectives ===
=== Objectives ===


* bbnn
* Develop test plans/scenarios for questionnaire_helper.rb
* Develop test plans/scenarios for review_bids_helper.rb
* Improve code coverage for questionnaire_helper.rb
* Improve code coverage for review_bids_helper.rb


=== Files Involved ===
=== Files Involved ===


* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb
* Review_bids_helper.rb: /app/helpers/review_bids_helper.rb
* Review_bids_helper.rb: /app/helpers/review_bids_helper.rb
* Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb


=== Mentor ===
=== Mentor ===
Line 26: Line 32:
* Sahil Santosh Sawant (ssawant2@ncsu.edu)
* Sahil Santosh Sawant (ssawant2@ncsu.edu)


== Hamer Algorithm ==
== Class and Method Overview ==
 
=== QuestionnaireHelper ===
 
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:
 
==== Constants ====
 
CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT
  - These constants define indices for specific columns in a CSV file.


The grading algorithm described in the paper is designed to provide a reward to reviewers who participate effectively by allocating a portion of the assignment mark to the review, with the review mark reflecting the quality of the grading. Here's an explanation of the algorithm:
QUESTIONNAIRE_MAP
  - This constant is a hash that maps questionnaire types to their respective questionnaire classes.
  - It's used by the `questionnaire_factory` method to determine the appropriate class to instantiate.


1. Review Allocation: Each reviewer is assigned a number of essays to grade. The paper suggests assigning at least five essays, with ten being ideal. Assuming each review takes 20 minutes, ten reviews can be completed in about three and a half hours.
==== Methods ====


2. Grading Process:
1. adjust_advice_size(questionnaire, question)
* Once the reviewing is complete, grades are generated for each essay and weights are assigned to each reviewer.
  - This method adjusts the size of advice associated with a given question in a questionnaire.
* The essay grades are computed by averaging the individual grades from all the reviewers assigned to that essay.
  - Parameters:
* Initially, all reviewers are given equal weight in the averaging process.
    - `questionnaire`: The questionnaire object.
* The algorithm assumes that some reviewers will perform better than others. It measures this by comparing the grades assigned by each reviewer to the averaged grades. The larger the difference between the assigned and averaged grades, the more out of step the reviewer is considered with the consensus view of the class.
    - `question`: The question object whose advice size needs adjustment.
* The algorithm adjusts the weighting of the reviewers based on this difference. Reviewers who are closer to the consensus view are given higher weights, while those who deviate significantly are given lower weights.
  - Functionality:
    - Checks if the question is a `ScoredQuestion`.
    - Deletes any existing advice for the question outside the score range.
    - Iterates over the score range and ensures each score has an associated advice.
    - Deletes any duplicate advice records.
 
2. update_questionnaire_questions
  - This method updates attributes of questionnaire questions based on form data, without modifying unchanged attributes.
  - Functionality:
    - Checks for presence of `params[:question]`.
    - Iterates through each question and its attributes in the parameters.
    - Compares each attribute's current value with the new value from the parameters and updates if changed.
    - Saves the question.


3. Iterative Process:
3. questionnaire_factory(type)
* The calculation of grades and weights is an iterative process. Each time the grades are calculated, the weights need to be updated, and each change in the weights affects the grades.
  - This method acts as a factory to create an appropriate questionnaire object based on the type provided.
* Convergence occurs quickly, typically requiring four to six iterations before a solution (a "fix-point") is reached.
  - Parameters:
    - `type`: The type of questionnaire.
  - Functionality:
    - Retrieves the questionnaire class from `QUESTIONNAIRE_MAP` based on the provided type.
    - If the type is not found in the map, it sets an error flash message.
    - Otherwise, it initializes a new instance of the corresponding questionnaire class and returns it.


4. Weight Adjustment:
=== ReviewBidsHelper ===
* The weights assigned to reviewers are adjusted based on the difference between the assigned and averaged grades. Reviewers with larger discrepancies have their weights adjusted inversely proportional to this difference.
* To prevent excessively large weights, a logarithmic dampening function is applied, allowing weights to rise to twice the class average before further increases are awarded sparingly.


5. Properties:
This Ruby module, `ReviewBidsHelper` serves as a helper module for views related to reviewing bids in expertiza. `ReviewBidsHelper` provides helper methods for rendering topic rows and determining the background color for topics based on their bid status and the number of participants. These methods are likely used in the views associated with reviewing bids in the application. Let's break down the class and its methods:
* The algorithm aims to identify and diminish the impact of "rogue" reviewers who may inject random or arbitrary grades into the peer assessment process.
* By adjusting reviewer weights based on their grading accuracy, the algorithm aims to improve the reliability of the grading process in the presence of such rogue reviewers.


Overall, the algorithm seeks to balance the contributions of different reviewers based on the accuracy of their grading, ultimately aiming to produce reliable grades for each essay in a peer assessment scenario.
==== Methods ====


== Hamer value calculation ==
1. get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)
[[File:Step1.PNG|400px]]
  - This method seems to be responsible for generating HTML markup for a row in a table displaying topics for review bids.
<br>
  - Parameters:
[[File:Step2.PNG|400px]]
    - `topic`: Represents a specific topic being reviewed.
<br>
    - `selected_topics`: An array of topics that have been selected.
[[File:Step3.PNG|400px]]
    - `num_participants`: The number of participants involved in the review process.
<br>
  - Functionality:
[[File:Step4.PNG|400px]]
    - Iterates through the `selected_topics`.
    - Depending on the conditions (whether the topic is selected and not waitlisted, or selected and waitlisted), it generates a specific row HTML.
    - Returns the generated row HTML as safe HTML.


== Objective 1: Develop code testing scenarios ==
2. get_topic_bg_color_review_bids(topic, num_participants)
We assumed 9 reviewers to review 4 submissions each to cover the following test scenarios:
  - This method calculates the background color for a topic based on the number of participants and the number of bids for that topic.
* 3 cases where reviewers are giving credible scores (passing1, passing2, passing3)
  - Parameters:
* case where reviewer is giving max scores (10) to all submissions (should be flagged)
    - `topic`: Represents the topic for which the background color is being determined.
* case where reviewer is giving min scores (0) to all submissions (should be flagged)
    - `num_participants`: The total number of participants.
* case where reviewer is giving median scores (5) to all submissions (should be flagged)
  - Functionality:
* case where reviewer is giving same scores to all submissions (should be flagged)
    - Calculates the number of bids for the given `topic`.
    - Determines the proportion of bids compared to the total number of participants and adjusts the color accordingly.
    - Returns a string representing the RGB value of the background color.


=== Object Creation ===
== Objective 1: Develop code testing scenarios for questionnaire_helper ==
Below is the Input object for tests that cover all the above scenarios:
* Single Responsibility Principle (SRP):
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.


* Open/Closed Principle (OCP):
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.
* Dependency Injection Principle (DIP):
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.
* Factory Method Pattern:
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.
After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:
* adjust_advice_size
* questionnaire_factory
* update_questionnaire_questions
=== adjust_advice_size ===
==== Method Description ====
The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.
==== Test Setup ====
Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice.
Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.
==== Test Contexts ====
* 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.
<pre>
<pre>
INPUTS_new = {
context 'when question is a ScoredQuestion' do
    "submission1": {
      it 'adjusts advice size based on questionnaire scores' do
     "maxtoall": 10,
     allow(QuestionAdvice).to receive(:where).and_return([])
     "mintoall": 1,
     allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))
    "mediantoall": 5,
     allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)
     "incomplete_review": 4,
     described_class.adjust_advice_size(questionnaire, scored_question)
     "max_incomplete": 10,
     expect(QuestionAdvice).to have_received(:where).exactly(10).times
     "sametoall":3,
     expect(scored_question.question_advices.size).to eq(10)
    "passing1": 10,
  end
     "passing2": 10,
     end
    "passing3": 9
 
    },
</pre>
      "submission2": {
* 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.
    "maxtoall": 10,
<pre>
    "mintoall": 1,
context 'when question is not a ScoredQuestion' do
    "mediantoall": 5,
       it 'does not adjust advice size' do
    "incomplete_review": 2,
        allow(QuestionAdvice).to receive(:where)
     "max_incomplete": 10,
        allow(QuestionAdvice).to receive(:new)
    "min_incomplete": 1,
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)
    "sametoall":3,
        described_class.adjust_advice_size(questionnaire, non_scored_question)
    "passing1": 3,
        expect(QuestionAdvice).not_to have_received(:where)
    "passing2": 2,
        expect(QuestionAdvice).not_to have_received(:new)
    "passing3": 4
       end
    },
     end
       "submission3": {
 
    "maxtoall": 10,
    "mintoall": 1,
    "mediantoall": 5,
    "sametoall":3,
    "passing1": 7,
    "passing2": 4,
    "passing3": 5
    },
       "submission4": {
    "maxtoall": 10,
    "mintoall": 1,
    "mediantoall": 5,
    "max_incomplete": 10,
    "min_incomplete": 1,
    "sametoall":3,
    "passing1": 6,
     "passing2": 4,
    "passing3": 5
    }
}.to_json
</pre>
</pre>


=== Expected Hamer Values ===
==== Expectations ====
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.
 
=== questionnaire_factory ===
 
==== Method Description ====
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.
 
==== Test Setup ====
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.
 
==== Test Contexts ====
 
* 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).
 
<pre>
context 'when given a valid type' do
      it 'returns an instance of the specified questionnaire type' do
        questionnaire_type = 'ReviewQuestionnaire'
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)
      end
 
</pre>
* 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.


<pre>
<pre>
EXPECTED = {
context 'when given an invalid type' do
    "Hamer": {
      it 'sets an error flash message' do
         "maxtoall": 2.65,
         questionnaire_type = 'UnknownQuestionnaire'
         "mintoall": 2.41,
         expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')
        "mediantoall": 1.03,
      end
        "incomplete_review": 2.31,
    end
        "max_incomplete": 2.57,
        "min_incomplete": 2.48,
        "sametoall":1.58,
        "passing1": 2.17,
        "passing2": 1.73,
        "passing3": 1.23,
    }
}.to_json
</pre>
</pre>


== Objective 2: Reimplement the algorithm if discrepancies arise in the reputation web server's Hamer values. ==
==== Expectations ====
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.
 
=== update_questionnaire_questions ===
We did not implement test cases for this method because it was already covered in last semester by previous team.
 
== Objective 2: Develop code testing scenarios for review_bids_helper ==
* Single Responsibility Principle (SRP):
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.


As established before, the values returned by reputation server do not match the expected values. Hence, we concluded that the PeerLogic Webservice is implemented incorrectly. In this phase, we implemented the algorithm in Ruby as a function in a controller file : /app/controllers/reputation_web_service_controller.rb
* Factory Method Pattern:
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.


=== Changes made in implementation ===
After reviewing the ReviewBidsHelper module, we identified two methods that require testing:
* coded this algorithm in Ruby in a controller.
* Included a way for the algorithm to handle nil values.


=== Code Snippet ===
=== get_intelligent_topic_row_review_bids ===
<pre>
==== Objective ====
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.


# Method: calculate_reputation_score
==== Test Setup ====
# This method calculates the reputation scores for each reviewer based on the provided input data.
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.
# It first parses the input JSON string to extract the submissions and their corresponding scores.
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.
# Then, it calculates the average weighted grades per reviewer and the delta R values.
# Next, it calculates the weight prime values based on the delta R values.
# Finally, it calculates the reputation weights for each reviewer using the weight prime values.
#
# Params
#  - input_json: a JSON string representing the input data with submission scores
#
# Returns
#  An array of reputation scores, one score per reviewer, indicating their reputation in the system.
def calculate_reputation_score(reviews)
  # Parse the input JSON string
  # reviews = JSON.parse(input_json)


  # Initialize arrays to store intermediate values
==== Test Contexts ====
  grades = []
  delta_r = []
  weight_prime = []
  weight = []


  # Calculate Average Weighted Grades per Reviewer
* 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.
  reviews.each do |reviewer_marks|
<pre>
    # Skip nil values when calculating the sum
context 'when selected topics are present' do
    reviewer_marks_without_nil = reviewer_marks.compact
      it 'returns HTML code for topic row with appropriate background color' do
    assignment_grade_average = reviewer_marks_without_nil.sum.to_f / reviewer_marks_without_nil.length
        selected_topic = instance_double('SelectedTopic')
    grades << assignment_grade_average
        allow(selected_topic).to receive(:topic_id).and_return(1)
  end
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)
        selected_topics = [selected_topic]


  # Calculate delta R
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="yellow">')
  reviews.each do |reviewer_marks|
       end
    reviewer_delta_r = 0
    # Skip nil values when calculating the sum
    reviewer_marks_without_nil = reviewer_marks.compact
    reviewer_marks_without_nil.each_with_index do |grade, student_index|
       reviewer_delta_r += (grade - grades[student_index]) ** 2
     end
     end
    delta_r << reviewer_delta_r / reviewer_marks_without_nil.length
</pre>
  end
* 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.
<pre>
context 'when selected topic is waitlisted' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topic = instance_double('SelectedTopic')
        allow(selected_topic).to receive(:topic_id).and_return(1)
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)
        selected_topics = [selected_topic]


  # Calculate weight prime
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="lightgray">')
  average_delta_r = delta_r.sum / delta_r.length.to_f
      end
    end
</pre>
* 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.
<pre>
context 'when selected topics are not present' do
      it 'returns HTML code for topic row with appropriate background color' do
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')
        selected_topics = []


  delta_r.each do |reviewer_delta_r|
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr id="topic_1" style="background-color:rgb(255,255,255)">')
     weight_prime << average_delta_r / reviewer_delta_r
      end
  end
     end
</pre>
* 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.
<pre>
context 'when selected topics are nil' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topics = nil


  # Calculate reputation weight
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr id="topic_1" style="background-color:rgb(47,352,0)">')
  weight_prime.each do |reviewer_weight_prime|
      end
    if reviewer_weight_prime <= 2
      weight << reviewer_weight_prime.round(2)
    else
      weight << (2 + Math.log(reviewer_weight_prime - 1)).round(2)
     end
     end
   end
   end


  # Return the reputation weights
  weight
  end
end
</pre>
</pre>
==== Expectations ====
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.


== Objective 3: Validate the accuracy of the newly implemented Hamer algorithm ==


We test the newly implemented Hamer algorithm function with our scenarios and verify if they match the expected values.
=== get_topic_bg_color_review_bids ===


=== Test Code Snippet ===
==== Objective ====
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.


==== Test Setup ====
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.


==== Test Contexts ====
* 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.
<pre>
<pre>
describe ReputationWebServiceController do
context 'when there are no review bids' do
    it "should calculate correct Hamer calculation" do
      it 'returns default RGB color code' do
      weights = ReputationWebServiceController.new.calculate_reputation_score(reviews)
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])
      keys = ["maxtoall", "mintoall", "mediantoall", "incomplete_review", "sametoall", "passing1", "passing2", "passing3"]
 
      rounded_weights = weights.map { |w| w.round(1) }
        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')
      result_hash = keys.zip(rounded_weights).to_h
      end
      expect(result_hash).to eq(JSON.parse(EXPECTED)["Hamer"])
     end
     end
end
 
</pre>
</pre>
* 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.
<pre>
context 'when selected topic is waitlisted' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topic = instance_double('SelectedTopic')
        allow(selected_topic).to receive(:topic_id).and_return(1)
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)
        selected_topics = [selected_topic]


=== Results===
        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="lightgray">')
      end
    end
</pre>


[[File:Results hamer new.jpeg]]
==== Expectations ====
Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.


=== Conclusion ===
== Coverage Results ==
The observed results indicate a tendency towards lower values, primarily due to our decision to include nil values and treat them as zeros in our analysis. This treatment has led to a skew in the scores, favoring lower values and potentially impacting the accuracy of our findings. To address this issue and improve the robustness of our analysis, it is advisable to explore alternative approaches such as using median or random values instead of treating nil values as zeros. However, we must also carefully consider how to handle incomplete reviews that contain nil values in our input dataset, as this can significantly influence the overall integrity and reliability of our results and conclusions.
=== Questionnaire_helper ===
* Previous coverage: 66%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Fquestionnaire_helper.rb
* Current coverage:


== Conclusion ==
[[File:Questionnaire helper spec coverage.jpg]]
In this project, we aimed to test the accuracy of the Hamer algorithm used for assessing the credibility of reviewers in a peer assessment system. We began by developing code testing scenarios to validate the Hamer algorithm and ensure the accuracy of its output values. These scenarios covered various review scenarios, including cases where reviewers provided extreme scores.


It was established that the original reputation web server was implemented incorrectly.
=== Review bids helper ===
* Previous coverage: 17.65%: https://coveralls.io/builds/66490075/source?filename=app%2Fhelpers%2Freview_bids_helper.rb
* Current coverage:


As a result, we proceeded to reimplement the Hamer algorithm in Ruby, incorporating adjustments to handle nil values appropriately. Subsequently, we validated the accuracy of the newly implemented algorithm using the same testing scenarios. While the results initially showed a skew towards lower values due to our treatment of nil values, we acknowledge the need for further refinement to handle these cases more effectively.
[[File:Review bid helper spec coverage.jpg]]


In conclusion, this project highlights the importance of rigorous testing and implementation adjustments in ensuring the reliability of algorithms used in peer assessment systems. Moving forward, we recommend further refinements and validations to enhance the accuracy and robustness of the Hamer algorithm.
== Conclusion ==
This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper and review_bids_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Notably, significant improvements were achieved in the review_bids_helper.rb file, with comprehensive test plans substantially increasing code coverage. 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.


==Links==
==Links==
Link to Expertiza repository: [https://github.com/expertiza/expertiza here]
* Link to Expertiza repository: [https://github.com/expertiza/expertiza here]
 
Link to the forked repository: [https://github.com/Prachit99/expertiza/tree/main here]
 
Link to pull request: [https://github.com/expertiza/expertiza/pull/2778 here]


Link to Github Project page: [https://github.com/users/Prachit99/projects/1 here]
* Link to Testing video: [https://youtu.be/jDfmPUgDDXA]


Link to Testing Video: [https://drive.google.com/file/d/1gZ5iDgqMW3COOT9Uw-_yhJlwJLZDxz-s/view?usp=sharing here]
* Link to pull request : [https://github.com/expertiza/expertiza/pull/2799 here]


== References ==
== References ==

Latest revision as of 03:52, 24 April 2024

This page describes the changes made for the Spring 2024 E2440. Testing for questionnaire_helper, review_bids_helper

Project Overview

Problem Statement

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.

Current Code Coverage

Objectives

  • Develop test plans/scenarios for questionnaire_helper.rb
  • Develop test plans/scenarios for review_bids_helper.rb
  • Improve code coverage for questionnaire_helper.rb
  • Improve code coverage for review_bids_helper.rb

Files Involved

  • Questionnaire_helper.rb: /app/helpers/questionnaire_helper.rb
  • Review_bids_helper.rb: /app/helpers/review_bids_helper.rb

Mentor

  • Muhammet Mustafa Olmez (molmez@ncsu.edu)

Team Members

  • Neha Vijay Patil (npatil2@ncsu.edu)
  • Prachit Mhalgi (psmhalgi@ncsu.edu)
  • Sahil Santosh Sawant (ssawant2@ncsu.edu)

Class and Method Overview

QuestionnaireHelper

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:

Constants

CSV_QUESTION, CSV_TYPE, CSV_PARAM, CSV_WEIGHT

  - These constants define indices for specific columns in a CSV file.

QUESTIONNAIRE_MAP

  - This constant is a hash that maps questionnaire types to their respective questionnaire classes.
  - It's used by the `questionnaire_factory` method to determine the appropriate class to instantiate.

Methods

1. adjust_advice_size(questionnaire, question)

  - This method adjusts the size of advice associated with a given question in a questionnaire.
  - Parameters:
    - `questionnaire`: The questionnaire object.
    - `question`: The question object whose advice size needs adjustment.
  - Functionality:
    - Checks if the question is a `ScoredQuestion`.
    - Deletes any existing advice for the question outside the score range.
    - Iterates over the score range and ensures each score has an associated advice.
    - Deletes any duplicate advice records.
  

2. update_questionnaire_questions

  - This method updates attributes of questionnaire questions based on form data, without modifying unchanged attributes.
  - Functionality:
    - Checks for presence of `params[:question]`.
    - Iterates through each question and its attributes in the parameters.
    - Compares each attribute's current value with the new value from the parameters and updates if changed.
    - Saves the question.

3. questionnaire_factory(type)

  - This method acts as a factory to create an appropriate questionnaire object based on the type provided.
  - Parameters:
    - `type`: The type of questionnaire.
  - Functionality:
    - Retrieves the questionnaire class from `QUESTIONNAIRE_MAP` based on the provided type.
    - If the type is not found in the map, it sets an error flash message.
    - Otherwise, it initializes a new instance of the corresponding questionnaire class and returns it.

ReviewBidsHelper

This Ruby module, `ReviewBidsHelper` serves as a helper module for views related to reviewing bids in expertiza. `ReviewBidsHelper` provides helper methods for rendering topic rows and determining the background color for topics based on their bid status and the number of participants. These methods are likely used in the views associated with reviewing bids in the application. Let's break down the class and its methods:

Methods

1. get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)

  - This method seems to be responsible for generating HTML markup for a row in a table displaying topics for review bids.
  - Parameters:
    - `topic`: Represents a specific topic being reviewed.
    - `selected_topics`: An array of topics that have been selected.
    - `num_participants`: The number of participants involved in the review process.
  - Functionality:
    - Iterates through the `selected_topics`.
    - Depending on the conditions (whether the topic is selected and not waitlisted, or selected and waitlisted), it generates a specific row HTML.
    - Returns the generated row HTML as safe HTML.

2. get_topic_bg_color_review_bids(topic, num_participants)

  - This method calculates the background color for a topic based on the number of participants and the number of bids for that topic.
  - Parameters:
    - `topic`: Represents the topic for which the background color is being determined.
    - `num_participants`: The total number of participants.
  - Functionality:
    - Calculates the number of bids for the given `topic`.
    - Determines the proportion of bids compared to the total number of participants and adjusts the color accordingly.
    - Returns a string representing the RGB value of the background color.

Objective 1: Develop code testing scenarios for questionnaire_helper

  • Single Responsibility Principle (SRP):

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.

  • Open/Closed Principle (OCP):

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.

  • Dependency Injection Principle (DIP):

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.

  • Factory Method Pattern:

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.

After reviewing the QuestionnaireHelper class, we identified three methods in the class that require testing. These methods are as follows:

  • adjust_advice_size
  • questionnaire_factory
  • update_questionnaire_questions

adjust_advice_size

Method Description

The .adjust_advice_size method is being described. This method likely adjusts the size of advice related to questions based on certain conditions.

Test Setup

Mock objects are set up using let statements. These include a questionnaire, a scored_question, a non_scored_question, and a question_advice. Each of these mock objects is prepared with predefined attributes that mimic the behavior of actual objects in the system.

Test Contexts

  • 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.
 context 'when question is a ScoredQuestion' do
      it 'adjusts advice size based on questionnaire scores' do
    allow(QuestionAdvice).to receive(:where).and_return([])
    allow(QuestionAdvice).to receive(:new).and_return(double('QuestionAdvice', save: true))
    allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(true)
    described_class.adjust_advice_size(questionnaire, scored_question)
    expect(QuestionAdvice).to have_received(:where).exactly(10).times
    expect(scored_question.question_advices.size).to eq(10)
  end
    end

  • 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.
context 'when question is not a ScoredQuestion' do
      it 'does not adjust advice size' do
        allow(QuestionAdvice).to receive(:where)
        allow(QuestionAdvice).to receive(:new)
        allow(scored_question).to receive(:is_a?).with(ScoredQuestion).and_return(false)
        described_class.adjust_advice_size(questionnaire, non_scored_question)
        expect(QuestionAdvice).not_to have_received(:where)
        expect(QuestionAdvice).not_to have_received(:new)
      end
    end

Expectations

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.

questionnaire_factory

Method Description

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.

Test Setup

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.

Test Contexts

  • 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).
 context 'when given a valid type' do
      it 'returns an instance of the specified questionnaire type' do
        questionnaire_type = 'ReviewQuestionnaire'
        expect(helper.questionnaire_factory(questionnaire_type)).to be_an_instance_of(ReviewQuestionnaire)
      end

  • 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.
context 'when given an invalid type' do
      it 'sets an error flash message' do
        questionnaire_type = 'UnknownQuestionnaire'
        expect { helper.questionnaire_factory(questionnaire_type) }.to change { flash[:error] }.from(nil).to('Error: Undefined Questionnaire')
      end
    end

Expectations

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.

update_questionnaire_questions

We did not implement test cases for this method because it was already covered in last semester by previous team.

Objective 2: Develop code testing scenarios for review_bids_helper

  • Single Responsibility Principle (SRP):

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.

  • Factory Method Pattern:

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.

After reviewing the ReviewBidsHelper module, we identified two methods that require testing:

get_intelligent_topic_row_review_bids

Objective

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.

Test Setup

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

Test Contexts

  • 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.
context 'when selected topics are present' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topic = instance_double('SelectedTopic')
        allow(selected_topic).to receive(:topic_id).and_return(1)
        allow(selected_topic).to receive(:is_waitlisted).and_return(false)
        selected_topics = [selected_topic]

        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="yellow">')
      end
    end
  • 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.
context 'when selected topic is waitlisted' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topic = instance_double('SelectedTopic')
        allow(selected_topic).to receive(:topic_id).and_return(1)
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)
        selected_topics = [selected_topic]

        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="lightgray">')
      end
    end
  • 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.
context 'when selected topics are not present' do
      it 'returns HTML code for topic row with appropriate background color' do
        allow(helper).to receive(:get_topic_bg_color_review_bids).and_return('rgb(255,255,255)')
        selected_topics = []

        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr id="topic_1" style="background-color:rgb(255,255,255)">')
      end
    end
  • 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.
context 'when selected topics are nil' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topics = nil

        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr id="topic_1" style="background-color:rgb(47,352,0)">')
      end
    end
  end

Expectations

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.


get_topic_bg_color_review_bids

Objective

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.

Test Setup

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.

Test Contexts

  • 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.
 context 'when there are no review bids' do
      it 'returns default RGB color code' do
        allow(ReviewBid).to receive(:where).with(signuptopic_id: topic.id).and_return([])

        expect(helper.get_topic_bg_color_review_bids(topic, num_participants)).to eq('rgb(47,352,0)')
      end
    end

  • 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.
context 'when selected topic is waitlisted' do
      it 'returns HTML code for topic row with appropriate background color' do
        selected_topic = instance_double('SelectedTopic')
        allow(selected_topic).to receive(:topic_id).and_return(1)
        allow(selected_topic).to receive(:is_waitlisted).and_return(true)
        selected_topics = [selected_topic]

        expect(helper.get_intelligent_topic_row_review_bids(topic, selected_topics, num_participants)).to include('<tr bgcolor="lightgray">')
      end
    end

Expectations

Each test contains an it block with an expectation. These expectations use expect statements to verify the behavior of the method under different conditions.

Coverage Results

Questionnaire_helper

Review bids helper

Conclusion

This document outlines a thorough plan to enhance testing and code coverage for the questionnaire_helper and review_bids_helper files in Expertiza. With defined objectives, including developing detailed test plans and scenarios, the project aims to address current code coverage gaps. Notably, significant improvements were achieved in the review_bids_helper.rb file, with comprehensive test plans substantially increasing code coverage. 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.

Links

  • Link to Expertiza repository: here
  • Link to Testing video: [1]
  • Link to pull request : here

References

1. Expertiza on GitHub (https://github.com/expertiza/expertiza)
2. The live Expertiza website (http://expertiza.ncsu.edu/)
3. Pluggable reputation systems for peer review: A web-service approach (https://doi.org/10.1109/FIE.2015.7344292)