CSC/ECE 517 Fall 2021 - E2133. Write tests for popup controller.rb

From Expertiza_Wiki
Jump to navigation Jump to search

The goal of this project is to add unit testing for the PopupController to raise statement coverage above 60%. See the Test Outline section for specifics of our work.

Project Introduction

The PopupController is responsible for preparing data that will be displayed in popup views. To render the data, which mostly concerns assignments, the controller makes many accesses to database tables such as Response, ResponseMap, Question, Questionnaire, Answer, and Participant. The previous team implemented some of the test cases to improve the coverage. The previous work test coverage was 96%. However, their test cases followed integration testing methodologies which were not consistent with Unit testing. We will be implementing Unit testing while retaining the current coverage.

Team

Yi Qiu (mentor)

  • Sayali Parab, snparab1704
  • Rageeni Sah, ragesah
  • Priya Jakhar, priyajakhar

Files Involved

popup_controller.rb

popup_controller_spec.rb

Running Tests

What we need to do is to set up the environment and complete the 'popup_controller_spec.rb' to finish the unit tests for popup controller. This rspec test file can be run by calling the following code:

  rspec spec/controllers/popup_controller_spec.rb

What needs to be done

1. Write RSpec unit test cases to make the statement coverage above 90%.

2. Cover as many boundary cases and the core functionality for all popup controller actions.


Test Plan

In total, we wrote tests to cover all 6 methods in the popup controller. We created unit test cases with the help of mocked objects (factory objects and stub objects).


Popup Controller Methods

The code of the controller can be found here. The methods are:

  • action_allowed?
  • author_feedback_popup
  • team_users_popup
  • view_review_scores_popup
  • reviewer_details_popup
  • self_review_popup

Mock Models

Test Frame

In case of controllers, unit testing is about testing the functional logic.

1. action_allowed? - This is the first action that takes place when user tries to access the popup. This checks whether the current user is authorized to view the pop up. The action is allowed for all users except the student.

Code snippet:

  describe '#action_allowed?' do
    context 'when user does not have right privilege, it denies action' do
      it 'for no user' do; expect(controller.send(:action_allowed?)).to be false; end
      it 'for student' do
        stub_current_user(student, student.role.name, student.role)
        expect(controller.send(:action_allowed?)).to be false
      end
    end
    context 'when role has right privilege, it allows action' do
      it 'for Super Admin' do
        stub_current_user(superadmin, superadmin.role.name, superadmin.role)
        expect(controller.send(:action_allowed?)).to be true
      end
      it 'for Admin' do
        stub_current_user(admin, admin.role.name, admin.role)
        expect(controller.send(:action_allowed?)).to be true
      end
      it 'for Instructor' do
        stub_current_user(instructor, instructor.role.name, instructor.role)
        expect(controller.send(:action_allowed?)).to be true
      end
      it 'for TA' do
        stub_current_user(ta, ta.role.name, ta.role)
        expect(controller.send(:action_allowed?)).to be true
      end
    end
  end

2. author_feedback_popup - This action can be called from response_report by clicking on Student names from Instructor's end.

Code snippet:

  describe '#author_feedback_popup' do
    context 'when response_id does not exist' do
      it 'fail to get any info' do; expect(controller.send(:author_feedback_popup)).to be nil; end
    end
    context 'when response_id exists' do
      it 'passes tests in first block' do        
        allow(Question).to receive_message_chain(:find, :questionnaire_id).with(answer.question_id).with(no_args).and_return(questionnaire.id)
        allow(Questionnaire).to receive(:find).with(questionnaire.id).and_return(questionnaire)
        allow(questionnaire).to receive(:max_question_score).and_return(20)
        allow(Answer).to receive(:where).with(any_args).and_return([answer]) 
        allow(Response).to receive(:find).with(any_args).and_return(response) 
        allow(response).to receive(:average_score).and_return(16)  
        allow(response).to receive(:aggregate_questionnaire_score).and_return(20)
        allow(response).to receive(:maximum_score).and_return(19)
        allow(Participant).to receive(:find).with("1").and_return(participant)
        allow(User).to receive(:find).with(participant.user_id).and_return(participant)

        params = {response_id: 1, reviewee_id: 1}
        session = {user: ta}
        result = get :author_feedback_popup, params, session
        expect(result.status).to eq 200
        expect(controller.instance_variable_get(:@sum)).to eq 20
        expect(controller.instance_variable_get(:@total_possible)).to eq 19
        expect(controller.instance_variable_get(:@total_percentage)).to eq 16
      end
    end
  end

3. team_users_popup - This action can be called from _review_report by clicking the team reviewed link from the instructor's end. This action is responsible to fetch team details that have reviewed the assignment. details: team name, team users, max score round, total possible rounds, comments, etc.

Findings: team_users_popup.html

Params: id, id2

Responses: sum team assignment team_users reviewer_id similar_assignments

Used variables in HTML view: team, team_users, ip, assignment, reviewer_id, response_round_ , response_id_round_ , scores_round_ , Max_score_round_ , Total_percentage_round_ , sum_round_ , total_possible_round_

Code snippet:

describe '#team_users_popup' do
    ## INSERT CONTEXT/DESCRIPTION/CODE HERE
    it "renders the page successfuly as Instructor" do
      allow(Team).to receive(:find).and_return(team)
      allow(Assignment).to receive(:find).and_return(assignment)

      reviewer = double(:reviewer1, reviewer_id: 1)
      user_id = double(:user_id, user_id: 1)
      response = double('response1', response_id: 1, responses: "this is test response")
      answer = double('answer', question_id: 1)

      allow(ResponseMap).to receive(:find).with(1).and_return(reviewer)
      allow(Participant).to receive(:find).with(1).and_return(user_id)
      allow(Response).to receive_message_chain(:where, :last).with(map_id: 1, round: 1).with(no_args).and_return(response)
      allow(Answer).to receive(:where).with(response_id: 1).and_return(answer)
      allow(Response).to receive_message_chain(:average_score).with(no_args).and_return(response)
      allow(Response).to receive_message_chain(:aggregate_questionnaire_score).with(no_args).and_return(response)
      allow(Response).to receive_message_chain(:maximum_score).with(no_args).and_return(response)
      
      params = {id: team.id}
      session = {user: instructor}
      result = get :team_users_popup, params, session
      expect(result.status).to eq 200
    end
  end

4. view_review_scores_popup- This action can be called from _review_report by clicking the summary link from the instructor's end. This action is responsible to fetch the details of the scores that have been received for the assignment which includes further details such as the course, assignment, questions, responses, the team responsible for the review, review summary, etc.

Findings:

View_review_scores_popup.html

Params: reviewer_id, Assignment_id

Response: review_final_versions,

Used variables in HTML view: review_final_versions, review_final_versions.questionnaire_id, review_final_versions.response_ids

Test cases: If Reviewer_id is null, If Assignment_id is null, Assignment.vary_by_topic true - Provide Flash error, Assignment.vary_by_topic false - Does not provide Flash error, review_final_versions - Has the structure as expected by the rendering view page

Code snippet:

  describe '#view_review_scores_popup'do
    context 'review tone analysis operation is performed' do
      it 'Prepares scores and review analysis report for rendering purpose' do
        allow(Assignment).to receive(:find).and_return(assignment)
        allow(Participant).to receive(:find).and_return(participant)
        params = {reviewer_id: participant.id, assignment_id: assignment.id}
        session = {user: instructor}
        result = get :view_review_scores_popup, params, session
        expect(controller.instance_variable_get(:@review_final_versions)).to eq final_versions
      end
    end
  end

5. reviewer_details_popup - This action can be called from response_report by clicking reviewer names from the instructor's end. This action is responsible to fetch reviewer details: full name, user id, email, and handle.

Code snippet:

describe '#reviewer_details_popup' do
    render_views
    it "render reviewer_details_popup page successfully" do
      participant = double(:participant, user_id: 1)
      user = double(:user, fullname: "Test User", name: "Test", email: "test@gmail.com", handle: 1)
      allow(Participant).to receive(:find).with("1").and_return(participant)
      allow(User).to receive(:find).with(participant.user_id).and_return(user)
      params = {id: 1, assignment_id: 1}
      session = {user: instructor}
      get :reviewer_details_popup, params, session
      expect(@response).to have_http_status(200)
      expect(user.fullname).to eq("Test User")
      expect(user.name).to eq("Test")
      expect(user.email).to eq("test@gmail.com")
      expect(user.handle).to eq(1)
    end
  end

6. self_review_popup - This action can be called from "response_report" by clicking reviewer names from the instructor's end. In this action for the given response id maximum score is displayed. If the response id is nil maximum score is set to 5, i.e., if a question response is not reviewed then the system automatically assigns five as the maximum score for the response.

Code snippet:

describe '#self_review_popup' do
    context "when current user is the participant" do
      it "render page successfully as Instructor to get maximum_score " do
        question1 = double(:question1, questionnaire_id: 1)
        questionnaire1 = double(:questionnaire1, min_question_score: 1, max_question_score: 3, name: 'Test questionnaire')
        answer1 = double('Answer', question_id: 1)
        response1 = double('response1', response_id: 1, responses: "this is test response")
        allow(Answer).to receive_message_chain(:where, :first).with(response_id: 1).with(no_args).and_return(answer1)
        allow(Question).to receive(:find).with(1).and_return(question1)
        allow(Questionnaire).to receive(:find).and_return(questionnaire1)
        allow(Answer).to receive(:where).with("1").and_return(answer1)
        allow(Response).to receive_message_chain(:find, :average_score).with("1").with(no_args).and_return(response1)
        allow(Response).to receive_message_chain(:find, :aggregate_questionnaire_score).with("1").with(no_args).and_return(response1)
        allow(Response).to receive_message_chain(:find, :maximum_score).with("1").with(no_args).and_return(response1)
        params = {response_id: 1, user_fullname: questionnaire1.name}
        session = {user: instructor}
        get :self_review_popup, params, session
        expect(@response).to have_http_status(200)
      end
    end
  end

Results

The total coverage of the test is 75.59%, meeting our minimum coverage requirement.

Related Links

A video of all tests running can be seen here

The main repository can be found here

The forked git repository for this project can be found here

Conclusion

There were 6 modules in the controller for which we wrote unit tests following behavior driven approach. We did not consider any edge cases for now therefore there is scope to further improve the testing coverage to reach 100%.