CSC/ECE 517 Spring 2022 - E2209: Testing for analytic helper.rb, join team requests helper.rb

From Expertiza_Wiki
Revision as of 23:44, 21 March 2022 by Mkmarti5 (talk | contribs) (→‎Testing normalize(): Updated formatting for lists.)
Jump to navigation Jump to search


About Expertiza

Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.

Project Description

We were given the following problem statement as a starting point for our project: Refer to general testing guidelines above, but start by using the spec/helpers/join_team_request_helper_spec.rb file and the test that has already been written as a jumping-off point.

Testing join_team_request_helper.rb

This was the first portion of the project that our team decided to work on. The JoinTeamRequestHelper class is very simple, containing only a single method, display_request_status(). This method is used in two locations within the project, and has the purpose of displaying a better formatted string for the status of a JoinTeamRequest. The status of a JoinTeamRequest uses the following formatting: ‘P’ indicates a Pending status. ‘D’ indicates a Denied status. ‘A’ indicates an Accepted status. Any other status is unexpected, and should just be returned as it is to the caller. The code for this method is as follows:

def display_request_status(join_team_request)
 status = case join_team_request.status
          when 'P'
            'Pending: A request has been made to join this team.'
          when 'D'
            'Denied: The team has denied your request.'
          when 'A'
            "Accepted: The team has accepted your request.\nYou should receive an invitation in \"Your Team\" page."
          else
            join_team_request.status
          end
 status
end

To test this method, we wanted to check the output given a JoinTeamRequest covering all the possible status values. To do so, we created a new JoinTeamRequest for each test case and assigned the status to ‘P’, ‘D’, and ‘A’ as appropriate and verified that the correct message was returned.

A sample of these tests is as follows:

# Accepted Status
    context 'when the request is accepted' do
      before(:each) do
        join_team_request1.status = 'A'
      end

      it 'returns the associated status' do
        expect(helper.display_request_status(join_team_request1)).to eq("Accepted: The team has accepted your request.\nYou should receive an invitation in \"Your Team\" page.")
      end
    end

The tests for this method can be found in /spec/helpers/join_team_request_helper_spec.rb.

Testing analytic_helper.rb

We were given the task to test analytic_helper.rb as part of our assignment. This class had modules defined inside such as LineChartHelper, BarChartHelper, ScatterPlotHelper , PieChartHelper and the final one being the AnalyticHelper. All the modules excluding the AnalyticHelper did not have a single method in them. The AnalyticHelper had four methods which were get_chart_data(), normalize(), distribution(), distribution_categories().

We were working on the testing of normalize(), distribution() and distribution_categories().

Testing normalize()

The normalize() method was a simple method which normalized a sequence of numbers passed through an array to values in the range [0,1]. The original code for this method can be seen below:

def normalize(array)
    normalized_array = []
    max = array.max
    array.each do |element|
      normalized_array << element.to_f / max
    end
    normalized_array
  end

Upon analysis of this code, it seemed that it would be necessary to test the method with several different types of input. These types of input are as follows:

1) A sorted array of positive integers.

2) A sorted array of both positive and negative integers.

3) An unsorted array of positive integers.

4) An unsorted array of both positive and negative integers.

5) A sorted array of positive floating point numbers.

6) A sorted array of both positive and negative floating point numbers.

7) An unsorted array of positive floating point numbers.

8) An unsorted array of both positive and negative floating point numbers.

Upon initial implementation of these tests, it was found that the method did not perform as expected for several use cases, especially present in those tests with negative numbers. The cause was found to be that the method was not properly calculating a normalized result for each input number. The method was modified to use the following formula for normalization:

Normalized Value = (Value - Min) / (Max - Min)

The use of this new formula allowed the method to properly scale values to fit the range [0,1] as intended. However, we identified several special cases that needed to be addressed in testing as well:

1) An array of length 1, containing only a single number.

2) An array of arbitrary length, but containing only copies of a single number.

3) An empty array.

4) A nil input.

5) An array containing values that are non-numeric.

6) An array containing nil values.

After adding tests for these special cases, further adjustments needed to be made to the method to ensure that it behaved appropriately. For input arrays containing only a single number, the code was modified such that these values get normalized to be output as the value 1, rather than the default calculation which would cause a divide by zero error under the new formula. The method was also modified to check that the input is not empty or nil before performing normalization calculations. Finally, the method was modified to raise a TypeError when an array is passed containing non-numeric or nil values. The final code for normalize() can be seen below:

def normalize(array)
    # Scales all values in the array using the following formula:
    # scaled_value = (value - min) / (max - min)
    # This gives us all values in the original array normalized on the range [0,1]

    # Handle special cases: nil, empty array, array of non-numbers
    return nil if array.nil?
    return [] if array.length == 0
    array.each do |element|
      raise TypeError.new('Array to normalize contained a non-numeric value.') if !element.is_a?(Numeric)
    end

    # Our initially empty return value
    normalized_array = []

    # Get appropriate values for scaling each value
    min = array.min
    scaled_max = array.max - min

    # Handle cases where there is only one number in the array
    if scaled_max == 0
      min = 0
      scaled_max = array.max
    end

    # Go through each element in the array and normalize it
    array.each do |element|
      scaled_element = element - min
      normalized_array << scaled_element.to_f / scaled_max
    end

    # Return constructed array
    normalized_array
  end

As will be mentioned later, these tests will not appear in the pull request to the main Expertiza repository due to the AnalyticHelper class being unused in the rest of the project.

Testing distribution() and distribution_categories()
Testing get_chart_data()

This method took four parameters as an input which were chart_type, object_type, object_id_list and data_type_list. The chart_type described the type of chart which we wanted to create such as bar, pie etc. The object_type was an internal data which described what was the chart created for like courses, assignments, teams etc. The third parameter was the list of id’s which we traverse through and created charts for all the assignments provided there. The fourth parameter passed the type of chart we wanted to create. There were some generic types for which the charts were getting created.

So, since the method received four parameters as input, the method must have been called from somewhere. We searched through the call hierarchy for the method and found out that the method was not getting called from anywhere and this module was not used in the entire codebase. We went through the commit history of expertiza and found out that the last commit for analytic_helper was made on October 25th, 2014. Here is the link to the commit:

[[1]]

So following this discovery, we went to Dr. Gehringer with the problem. He suggested that since the helper was not being used anywhere in the code base it will be safe to remove the helper. So we removed analytic_helper.rb from expertiza/beta.

Removing Unused Code for Charts and Graphs

Following our discovery that the code for AnalyticHelper is unused in the current implementation of the project, we discussed with Dr. Gehringer on how to proceed with the project. He directed us to work on removing unused code relating to charts and graphs within the project in place of further testing on AnalyticHelper, as this testing effort would be wasted on features not used in practice.

So, we started searching for other charts and graphs related modules, classes and gems in the expertiza. We discovered that the ChartHelper module was used only in the AnalyticHelper and since we have removed AnalyticHelper, it will be safe to remove ChartHelper as well. So we removed ChartHelper. All the other helpers related to charts and graphs were currently used in expertiza.

We found three gems which were related to charts namely googlecharts, gchartrb and chartjs-ror. All the gems except googlecharts were in use, so we decided to remove googlecharts gem.

Conclusion

We as a team divided tasks among ourselves and have done pair programming. We as a team analyzed and understood the methods and modules which were part of the task. Since, we were not able to test analytic_helper.rb, we figured out other dead code related to graphs and charts in expertiza and have removed that helpers and gems. Our existing tests that were initially created for analytic_helper.rb prior to our change in direction for the project can be seen in open pull requests on our Forked Github Repository (Linked below)

Links

- Forked Github Repository: [[2]]

- Video of Tests Running Successfully: [[3]]

- Open Pull Request to Expertiza Repo: [[4]]