CSC/ECE 517 Fall 2021 - E2167. Testing - Team Related Files

From Expertiza_Wiki
Jump to navigation Jump to search

Relevant links

Github Repo : Repository


About Expertiza

Expertiza is an open source project based on Ruby on Rails framework. It is a web based program that allows instructors to create and update/edit assignments/tasks, and then assigns them to students. Students then can submit, edit, and update their assignments, as well as peer review other student's assignments.


Problem statement and background

There are a total of 5 controllers related to team: teams_controller, teams_users_controller, student_teams_controller, join_team_requests_controller, and advertise_for_partner_controller. They don't have any tests written for them. They all need essentially the same kind of fixtures or mocks to enable testing, mainly: a couple of students that can be added to the team, teams on Assignment and Course levels, some join team requests that can be sent from one student to another student to join a particular team.


Approach Chosen & Why?

We have chosen to approach this by subdividing each controller in terms of scenarios which relate to different methods that the controller has. We have made a shared context file in the support folder of the spec directory to hold the file that has the stubbed objects that are being used across the different controllers. It also has the tests that are same across all controllers. We have chosen to bring about robustness of test by writing unit tests and functional tests using RSpec and Capybara respectively. Unit Tests ensured that the particular functionality is being tested in an isolated setup and various cases are being considered for all methods. The tests are written so that they check the functionality of the controller without causing any collaboration issues with other controller files.


Implementation

teams_shared.rb

  1. This controller can be found in spec/support
  2. It has all shared methods to be included in Teams related files.

Why this file? - There are a lot of methods common in the Teams Related Files, they basically use similar / same stubbed objects and methods. Implementing the teams_shared.rb file keeps the code DRY and makes use of good design principles.

Declared common stubbed objects

This creates single instances of superadmin, admin, instructor, teaching assistant, course and assignment. There are 2 instances of students, 5 instances of assignment teams, 2 instances of course teams, 4 instances of team join requests, and single instances of participant, node and team user.


shared_context 'object initializations' do
 let(:superadmin) { build_stubbed(:superadmin) }
 let(:admin) { build_stubbed(:admin) }
 let(:instructor) { build_stubbed(:instructor, id: 1) }
 let(:ta) { build_stubbed(:teaching_assistant) }
 let(:student1) { build_stubbed(:student, id: 1, name: 'student2065') }
 let(:student2) { build_stubbed(:student, id: 2) }
 let(:course1) { build_stubbed(:course, name: 'TestCourse', id:1, instructor_id: instructor.id) }
 let(:assignment1) { build_stubbed(:assignment, name: 'TestAssignment', id: 1) }
 let(:team1) { build_stubbed(:assignment_team, id: 1, name: 'wolfers',parent_id: assignment1.id) }
 let(:team2) { build_stubbed(:assignment_team, id: 2, parent_id: assignment1.id) }
 let(:team3) { build_stubbed(:assignment_team, id: 3, parent_id: assignment1.id) }
 let(:team4) { build_stubbed(:assignment_team, id: 4, parent_id: assignment1.id) }
 let(:team5) { build_stubbed(:course_team, id: 5, parent_id: course1.id) }
 let(:team6) { build_stubbed(:course_team, id: 6, parent_id: course1.id) }
 let(:team7) { build_stubbed(:assignment_team, name: 'test',  parent_id: course1.id) }
 let(:join_team_request1) { build_stubbed(:join_team_request, id: 1, team_id: team1.id, status: 'P') }
 let(:join_team_request2) { build_stubbed(:join_team_request, id: 2, team_id: team2.id, status: 'P',comments: "Any comment") }
 let(:join_team_request3) { build_stubbed(:join_team_request, id: 3, team_id: team2.id, status: 'D',comments: "Updated") }
 let(:invalidrequest) { build_stubbed(:join_team_request) }
 let(:participant) { build_stubbed(:participant, id: 1) }
 let(:node1) { build_stubbed(:assignment_node,  node_object_id: 1) }
 let(:team_user1) { build_stubbed(:team_user, team_id:1, user_id:1)}

action_allowed? method shared example tests

The action_allowed method is there in all controllers, so a shared example was created which allows the tests to run in scope of different controllers. It contains tests for access checks for superadmin, admin, instructor and ta users. Only the authorization of student changes between the team related files so that test was not included in here.


shared_context 'authorization check', :shared_context => :metadata do
 # Testing to check Super Admin access
 it 'superadmin credentials' do
   stub_current_user(superadmin, superadmin.role.name, superadmin.role)
   expect(controller.send(:action_allowed?)).to be true
 end
 # Testing to check Admin access
 it 'admin credentials' do
   stub_current_user(admin, admin.role.name, admin.role)
   expect(controller.send(:action_allowed?)).to be true
 end
 # Testing to check TA access
 it 'ta credentials' do
   stub_current_user(ta, ta.role.name, ta.role)
   expect(controller.send(:action_allowed?)).to be true
 end
 # Testing to check Instructor access
 it 'instructor credentials' do
   stub_current_user(instructor, instructor.role.name, instructor.role)
   expect(controller.send(:action_allowed?)).to be true
 end

How to use this shared file? Add a require statement:

require './spec/support/teams_shared.rb'

Add include_context statements in the controller.

include_context "object initializations"
include_context 'authorization check'

teams_controller.rb

How to run the test?

rspec spec/controllers/teams_controller_spec.rb

Scenario 1: Create teams with random names

  1. After checking for the team type and team size, call function to randomize the teams
  2. Print message "Random teams have been successfully created."
  3. Redirect to list of teams

 describe 'create teams method' do
   context 'when correct parameters are passed' do
     it 'creates teams with random names' do
       allow(Object).to receive_message_chain(:const_get, :find).with(any_args).and_return(assignment1)
       allow(Version).to receive_message_chain(:where, :last).with(any_args).and_return(0.1)
       para = {id: assignment1.id, team_size: 2}
       session = {user: instructor, team_type: "Assignment"}
       result = get :create_teams, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:action => 'list', :id => assignment1.id)
     end
   end
 end

Scenario 2: List the teams assignment wise or course wise

  1. Check the team type passed in params to see if it is Assignment or Course type
  2. Get the teams belonging to that Assignment or Course and list them all

 describe 'list method' do
   before(:each) { allow(Assignment).to receive(:find_by).and_return(assignment1) }
   context 'when type is Assignment' do
     it 'lists the teams for that Assignment' do
       params = {id: assignment1.id, type: 'Assignment'}
       session = {user: instructor}
       result = get :list, params, session
       expect(result.status).to eq 200
       expect(controller.instance_variable_get(:@assignment)).to eq assignment1
     end
   end
   context 'when type is Course' do
     it 'lists the teams for that Course' do
       params = {id: course1.id, type: 'Course'}
       session = {user: instructor}
       result = get :list, params, session
       expect(result.status).to eq 200
       expect(controller.instance_variable_get(:@assignment)).to eq nil
     end
   end
   context 'when type is not Assignment or Course' do
     it 'throws error' do
       params = {id: 52, type: 'Subject'}
       session = {user: instructor}
       result = get :list, params, session
       expect(result.status).to eq 200
       expect(controller.instance_variable_get(:@assignment)).to eq nil
     end
   end
 end

Scenario 3: Create a new empty team

  1. Check if a team already exists with the same name
  2. If it doesn’t, create a new team with a name
  3. If a team is created, print "The team <team_name> has been successfully created."
  4. Redirect to list team page
  5. If error is there, fail to create a new team and redirect to new team form page

 describe 'new method' do
   it 'creates a new team successfully when all parameters are provided correctly' do
     allow(Object).to receive_message_chain(:const_get, :find).with(any_args).and_return(assignment1)
     para = {id: assignment1.id}
     session = {user: ta, team_type: 'Assignment'}
     result = get :new, para, session
     expect(result.status).to eq 200
     expect(controller.instance_variable_get(:@parent)).to eq assignment1
   end
 end

Scenario 4: Update an existing team

  1. Check if a team with new suggested name already exists
  2. If it doesn’t, update and save the new team name
  3. Print "The team <new_team_name> has been successfully updated."
  4. Redirect to list team page
  5. If error is there, fail to update team and redirect to edit team form page

 describe 'update method' do
   it 'updates the team name' do
     allow(Team).to receive(:find).and_return(team1)
     allow(Assignment).to receive(:find).and_return(assignment1)
     para = { id: team1.id, team: {name: 'rando team'}}
     session = {user: ta, team_type: 'Assignment'}
     result = get :update, para, session
     expect(result.status).to eq 302
     expect(result).to redirect_to(:action => 'list', :id => assignment1.id)
   end
   # this test will fail even though it should normally pass, that's because it runs into an error at @team.save
   # RumtimeError: stubbed models are not allowed to access the database - AssignmentTeam#save()
 end

Scenario 5: Delete a team

  1. Check if there is a team in waitlist for the topic that the team to be deleted holds
  2. If there is a team on the waitlist, assign the topic to that team. If multiple teams are there, assign the topic to the first team in the waitlist.
  3. Delete the team’s records in team, teams_users and sign_up_team tables
  4. Print “"The team <team_name> has been successfully deleted."

 describe 'delete method' do
   before(:each) { request.env['HTTP_REFERER'] = root_url }
   context 'when called and team is nil' do
     it 'simply redirects back to the earlier page' do
       allow(Team).to receive(:find_by).and_return(nil)
       para = {id: 5}
       session = {user: instructor}
       result = get :delete, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to :back
       expect(controller.instance_variable_get(:@team)).to eq nil
     end
   end
   context 'when called and team is not nil and it does not hold a topic' do
     it 'deletes the team' do
       allow(Team).to receive(:find_by).and_return(team5)
       allow(Object).to receive_message_chain(:const_get, :find).and_return(course1)
       allow(team5).to receive(:destroy).and_return(nil)
       para = {id: 5}
       session = {user: instructor, team_type: 'CourseTeam'}
       result = get :delete, para, session
       expect(result.status).to eq 302
       expect(controller.instance_variable_get(:@team)).to eq team5
     end
   end
 end

Scenario 6: Copy existing teams from a course down to an assignment

  1. Find the course that the assignment belongs to
  2. If a course for the assignment doesn’t exist, give the error message "No course was found for this assignment."
  3. Otherwise fetch all the teams that exist in the course
  4. If no teams exist, give the error message "No teams were found when trying to inherit."
  5. Else copy all teams and assign them the new assignment id

 describe 'inherit method' do
   context 'called when assignment belongs to course and team is not empty' do
     it 'copies teams from course to the assignment' do
       allow(Assignment).to receive(:find).and_return(assignment1)
       allow(Course).to receive(:find).and_return(course1)
       allow(course1).to receive(:get_teams).and_return([team5, team6])
       para = {id: team5.id}
       session = {user: ta}
       result = get :inherit, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:controller => 'teams', :action => 'list', :id => assignment1.id)
     end
   end
   context 'called when assignment belongs to course but team is empty' do
     it 'flashes note' do
       allow(Assignment).to receive(:find).and_return(assignment1)
       allow(Course).to receive(:find).and_return(course1)
       para = {id: team5.id}
       session = {user: ta}
       result = get :inherit, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:controller => 'teams', :action => 'list', :id => assignment1.id)
     end
   end
   context 'called when assignment belongs to no course' do
     let(:fasg) { build_stubbed(:assignment, id: 1074, course_id: -2) }
     # a temporary assigment object is created with an abnormal course_id so that we can check the fail condition of the method
     it 'flashes error' do
       allow(Assignment).to receive(:find).and_return(fasg)
       allow(Course).to receive(:find).and_return(course1)
       para = {id: team5.id}
       session = {user: ta}
       result = get :inherit, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:controller => 'teams', :action => 'list', :id => fasg.id)
     end
   end
 end

Scenario 7: Copy existing team from assignment to course

  1. Find the assignment id to which the team belongs
  2. Find the course to which the assignment belongs
  3. If assignment doesn’t belong to a course, give the error message "This assignment is not <assignment_name> with a course."
  4. Otherwise copy team from assignment and assign it the course id
  5. Print success message "The team <team_name> was successfully copied to <course_name>"
  6. Redirect to list page

 describe 'bequeath method' do
   context 'called when assignment has a course' do
     it 'copies the teams from assignment to the course' do
       allow(AssignmentTeam).to receive(:find).and_return(team2)
       allow(Assignment).to receive(:find).and_return(assignment1)
       allow(Course).to receive(:find).and_return(course1)
       para = {id: team2.id}
       session = {user: ta}
       result = get :bequeath, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:controller => 'teams', :action => 'list', :id => assignment1.id)
     end
   end
   context 'called when assignment does not have a course' do
     let(:fasg) { build_stubbed(:assignment, id: 1074, course_id: -2) }
     # a temporary assigment object is created with an abnormal course_id so that we can check the fail condition of the method
     it 'throws an error and fails to copy the teams' do
       allow(AssignmentTeam).to receive(:find).and_return(team2)
       allow(Assignment).to receive(:find).and_return(fasg)
       para = {id: team2.id}
       session = {user: ta}
       result = get :bequeath, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:controller => 'teams', :action => 'list', :id => fasg.id)
     end
   end
 end

Scenario 8: Edit method

  1. Find the team with the 'id' present in params

 describe 'edit method' do
   it 'successfully returns the team with the given team id' do
     allow(Team).to receive(:find).and_return(team1)
     para = {id: team1.id}
     session = {user: ta}
     result = get :edit, para, session
     expect(result.status).to eq 200
     expect(controller.instance_variable_get(:@team)).to eq team1
   end
   # this method has only 1 line which is just to look up a team with the id present in the params
 end

Scenario 9: Create an empty team

  1. Find the parent (Assignment or Course) based on the team type which starts the session
  2. It checks whether a team with the same exists in that category
  3. If team exists, it throws TeamExistsError and redirects to 'new' team form page
  4. If team doesn't exist, it creates a new team
  5. Successful message is displayed and user is redirected to 'list' teams page

 describe 'create method' do
   context 'when invoked with a team which does not exist' do
     it 'creates it' do
       allow(Assignment).to receive(:find).and_return(assignment1)
       para = { id: assignment1.id, team: {name: 'rando team'}}
       session = {user: ta, team_type: 'Assignment'}
       result = get :create, para, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(:action => 'list', :id => assignment1.id)
     end
   end
 end

Test Pass/ Fail

Coverage


teams_users_controller.rb

Code correction in teams_users_controller.rb in list method: Team users list users page was not displaying users as expected and showed error for accessing @team.assignment_id. The error is rectified and corrected.

 def list
   @team = Team.find(params[:id])
   @assignment = Assignment.find(@team.parent_id)
   @teams_users = TeamsUser.page(params[:page]).per_page(10).where(["team_id = ?", params[:id]])
 end

The above code is corrected to following:

 def list
   @team = Team.find(params[:id])
   @assignment = Assignment.find(@team.parent_id)
   @teams_users = TeamsUser.page(params[:page]).per_page(10).where(["team_id = ?", params[:id]])
 end

How to run the test?

 rspec spec/controllers/teams_users_controller_spec.rb

Scenario 1: List users under the team

  1. When list icon against a course or assignment team is selected
  2. Render list of users under selected team.

 #Test team users list functionality
 describe '#list' do
   context 'when list is clicked' do
   it 'renders list of users under Assignment team teams#users' do
     allow(Team).to receive(:find).with('1').and_return(team1)
     allow(Assignment).to receive(:find).with(1).and_return(assignment1)
     @params = {id:1}
     session = {user: instructor}
     get :list, @params, session
     expect(response).to render_template(:list)
   end
   end
 end

Scenario 2: New user under the course or assignment team

  1. Sets the instance variable to team object

 #Test team users controller new method
 describe '#new' do
   it ' sets the instance variable to team object' do
     allow(Team).to receive(:find).with('1').and_return(team1)
     params = {id:  1}
     session = {user: instructor}
     get :new, params, session
     expect(controller.instance_variable_get(:@team)).to eq(team1)
   end
 end

Scenario 3: Create: Add new user to the selected team.

  1. Given user is not defined: When given user is defined, link is provided to create the user.
  2. Selected team to which user is being added belongs to an assignment: When user is not a participant of the assignment, link is provided to add the user to the assignment. When the assignment team already has maximum number of users, "Maximum users reached" notification is flashed.
  3. Selected team to which user is being added belongs to a course: When user is not a participant of the course, link is provided to add the user to the course. When the course team already has maximum number of users, "Maximum users reached" notification is flashed.
  4. Response is redirected to 'http://test.host/teams/list?id=1'.



Create context scenario 1:

  1. User that is not defined is added to assignment or course team.
  2. Page throws 'user not defined' error.
  3. Response is redirected to teams list page.

#Test adding new user to assignment or course team
 describe '#create' do
   context 'when user is added to assignment or course team' do
   it 'it throws error when user is not defined' do
     allow(User).to receive(:find_by).with(name: 'instructor6').and_return(nil)
     allow(Team).to receive(:find).with('1').and_return(team1)
     session = {user: admin}
     params = {
         user: {name: 'instructor6'}, id: 1
     }
     post :create, params, session
     expect(flash[:error]).to eq "\"instructor6\" is not defined. Please <a href=\"http://test.host/users/new\">create</a> this user before continuing."
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end


Create context scenario 2:

  1. User that is not participant of an assignment team is added to an assignment team.
  2. Page throws 'user not participant of the current assignment' error.
  3. Response is redirected to teams list page.

   context 'when user is added to assignment team' do
   it 'it throws error when user added is not a participant of the current assignment' do
     allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
     allow(Team).to receive(:find).with('1').and_return(team1)
     allow(AssignmentTeam).to receive(:find).with('1').and_return(team1)
     allow(Assignment).to receive(:find).with(1).and_return(assignment1)
     allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)
     session = {user: admin}
     params = {
         user: {name: 'student2065'}, id: 1
     }
     post :create, params, session
     expect(flash[:error]).to eq "\"student2065\" is not a participant of the current assignment. Please <a href=\"http://test.host/participants/list?authorization=participant&id=1&model=Assignment\">add</a> this user before continuing."
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end


Create context scenario 3:

  1. User is added to an assignment which already has maximum number of participants.
  2. Page throws 'team already has the maximum number of members' error.
  3. Response is redirected to teams list page.

   context 'when user is added to assignment team' do
   it 'it throws error when assignmentTeam has maximum number of participants' do
     allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
     allow(Team).to receive(:find).with('1').and_return(team1)
     allow(AssignmentTeam).to receive(:find).with('1').and_return(team1)
     allow(Assignment).to receive(:find).with(1).and_return(assignment1)
     allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(participant)
     allow_any_instance_of(Team).to receive(:add_member).with(any_args).and_return(false)
     session = {user: admin}
     params = {
         user: {name: 'student2065'}, id: 1
     }
     post :create, params, session
     expect(flash[:error]).to eq "This team already has the maximum number of members."
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end


Create context scenario 4:

  1. Valid user that is participant of an assignment team is added to the assignment team with members less than maximum numbers.
  2. The user gets successfully added to the team.
  3. Response is redirected to teams list page.

   context 'when user is added to assignment team' do
     it 'new user gets successfully added to the assignment' do
       allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
       allow(Team).to receive(:find).with(any_args).and_return(team1)
       allow(AssignmentTeam).to receive(:find).with('1').and_return(team1)
       allow(Assignment).to receive(:find).with(1).and_return(assignment1)
       allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(participant)
       allow_any_instance_of(Team).to receive(:add_member).with(any_args).and_return(true)
       allow(TeamsUser).to receive(:last).with(any_args).and_return(student1)
       session = {user: admin}
       params = {
           user: {name: 'student2065'}, id: 1
       }
       post :create, params, session
       expect(response).to redirect_to('http://test.host/teams/list?id=1')
     end
   end


Create context scenario 5:

  1. User that is not participant of a course team is added to the course team.
  2. Page throws 'user not participant of the current course' error.
  3. Response is redirected to teams list page.

   context 'when user is added to course team' do
   it 'it throws error when user added to course Team is not defined' do
     allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
     allow(Team).to receive(:find).with('5').and_return(team5)
     allow(CourseTeam).to receive(:find).with('5').and_return(team5)
     allow(Course).to receive(:find).with(1).and_return(course1)
     allow(CourseParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)
     session = {user: admin}
     params = {
         user: {name: 'student2065'}, id: 5
     }
     post :create, params, session
     expect(flash[:error]).to eq "\"student2065\" is not a participant of the current course. Please <a href=\"http://test.host/participants/list?authorization=participant&id=1&model=Course\">add</a> this user before continuing."
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end


Create context scenario 6:

  1. User is added to course which already has maximum number of participants.
  2. Page throws 'team already has the maximum number of members' error.
  3. Response is redirected to teams list page.

   context 'when user is added to course team' do
   it 'it throws error when courseTeam has maximum number of participants' do
     allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
     allow(Team).to receive(:find).with('5').and_return(team5)
     allow(CourseTeam).to receive(:find).with('5').and_return(team5)
     allow(Course).to receive(:find).with(1).and_return(course1)
     allow(CourseParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(participant)
     allow_any_instance_of(CourseTeam).to receive(:add_member).with(any_args).and_return(false)
     session = {user: admin}
     params = {
         user: {name: 'student2065'}, id: 5
     }
     post :create, params, session
     expect(flash[:error]).to eq "This team already has the maximum number of members."
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end


Create context scenario 7:

  1. Valid user that is participant of a course team is added to the course team with members less than the maximum numbers.
  2. The user gets successfully added to the team.
  3. Response is redirected to teams list page.

   context 'when user is added to course team' do
     it 'new user gets successfully added to course' do
       allow(User).to receive(:find_by).with(name: student1.name).and_return(student1)
       allow(Team).to receive(:find).with('5').and_return(team5)
       allow(CourseTeam).to receive(:find).with('5').and_return(team5)
       allow(TeamsUser).to receive(:create).with(user_id: 1, team_id: 5).and_return(double('TeamsUser', id: 1))
       allow(TeamNode).to receive(:find_by).with(node_object_id: 5).and_return(double('TeamNode', id: 1))
       allow(TeamUserNode).to receive(:create).with(parent_id: 1, node_object_id: 1).and_return(double('TeamUserNode', id: 1))
       allow(Course).to receive(:find).with(1).and_return(course1)
       allow(CourseParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(participant)
       allow_any_instance_of(CourseTeam).to receive(:add_member).with(any_args).and_return(true)
       session = {user: admin}
       params = {
           user: {name: 'student2065'}, id: 5
       }
       post :create, params, session
       expect(response).to redirect_to('http://test.host/teams/list?id=1')
     end
   end
 end

Scenario 4: Delete user from the selected team.

  1. User under assignment or course team is deleted: user association to the respective team is deleted and hence user is no longer part of the team.

#Test delete user from team
 describe '#delete' do
   context 'when user is deleted' do
   it 'it deletes the user and redirects to Teams#list page' do
     allow(TeamsUser).to receive(:find).with("1").and_return(teamUser)
     allow(Team).to receive(:find).with(teamUser.team_id).and_return(team1)
     allow(User).to receive(:find).with(teamUser.user_id).and_return(student1)
     @params = {id:1}
     session = {user: instructor}
     post :delete, @params, session
     expect(response).to redirect_to('http://test.host/teams/list?id=1')
   end
   end
 end

Scenario 5: Delete selected users from the selected team.

  1. Selected users under assignment or course team is deleted: for each user in the selected users, association to the respective team is deleted and hence user is no longer part of the team.
  2. Response is redirected to 'http://test.host/teams/list'.

#Test delete selected users from team
 describe '#delete_selected' do
   context 'when selected users are deleted' do
     it 'it deletes the selected users and redirects to Teams#list page' do
       allow(TeamsUser).to receive(:find).with("1").and_return([teamUser])
       allow(TeamsUser).to receive(:find).with("2").and_return([teamUser2])
       @params = {item:[1,2]}
       session = {user: instructor}
       post :delete_selected, @params, session
       expect(response).to redirect_to('http://test.host/teams_users/list')
     end
   end
 end

Test Pass/ Fail

Coverage

student_teams_controller.rb

The student teams controller is used to create teams, remove participants from teams and update team name. The following test cases are written to test the student_teams_controller.rb.

How to run the test?

 rspec spec/controllers/student_teams_controller_spec.rb

Scenario 1: Set the variables for view

  1. If the current user is student and their User Ids match, then the following values are set: @send_invs, @received_invs, @current_due_date, @users_on_waiting_list, @teammate_review_allowed
  2. If the current user is not student or the User Ids do not match, then the return from method.

describe '#view' do
   it 'sets the student' do
     allow(AssignmentParticipant).to receive(:find).with('12345').and_return student
     allow(student_teams_controller).to receive(:current_user_id?)
     allow(student_teams_controller).to receive(:params).and_return(student_id: '12345')
     allow(student).to receive(:user_id)
     student_teams_controller.view
   end
 end

Scenario 2: Create Student teams

  1. If team name is empty, flash an error message saying "Team name missing while creating team" and redirect to view_student_teams_path
  2. If team name is not empty and team name already in use, "Team name being created was already in use" and redirect to view_student_teams_path
  3. If team name is not empty and team name not in use, save team data, add logged in student to team and redirect to view_student_teams_path.

describe 'POST #create' do
   #before(:each) do
   # @student = AssignmentParticipant.new
   #end
   # When assignment team is empty it flashes a notice
   context 'when create Assignment team' do
     it 'flash notice when team is empty' do
       allow(AssignmentTeam).to receive(:where).with(name: , parent_id: 1).and_return([])
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       session = {user:student1}
       params = {
         student_id:1,
         team:{
           name:
         },
         action: 'create'
       }
       result= post :create, params, session
       expect(result.status).to eq 302
     end
   end
   #when all the team name is set correctly, create team
   context "create team" do
     it "saves the team when all the team name is set correctly" do
       allow(AssignmentNode).to receive(:find_by).with(node_object_id: 1).and_return(node1)
       allow(AssignmentTeam).to receive(:new).with(name: 'test', parent_id: 1).and_return(team7)
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(User).to receive(:find).with(1).and_return(team_user1)
       allow_any_instance_of(Team).to receive(:add_member).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       allow(team7).to receive(:save).and_return(true)
       session = {user:student1}
       params = {
         student_id:1,
         team:{
           name:'test'
         },
         action: 'create'
       }
       result= post :create, params, session
       expect(result.status).to eq(302)
     end
   end
   #when the team name is already in use, it flashes message
   context "name already in use" do
     it "flash notice when the team name is already in use" do
       allow(AssignmentTeam).to receive(:where).with(name: 'test', parent_id: 1).and_return(team7)
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       allow(team7).to receive(:empty?).and_return(false)
       session = {user:student1}
       params = {
         student_id:1,
         team:{
           name:'test'
         },
         action: 'create'
       }
       result= post :create, params, session
       expect(result.status).to eq 302
     end
   end
 end

Scenario 3: Update team name:

  1. Find the team that should be updated.
  2. If there are no matching teams, call team_created_successfully and redirect to view_student_teams_path
  3. If there is exactly one match, then call team_created_successfully and redirect to view_student_teams_path
  4. If there are more than one team, then show the following error message "Team name being updated to was already in use" and redirect to edit_student_teams_path.

describe '#update' do
   #When the name is not already present in the database, it updates the name
   context 'update team name when matching name not found' do
     it 'update name when the name is not already present in the database' do
       allow(AssignmentTeam).to receive(:where).with(name: 'test', parent_id: 1).and_return([])
       allow(Team).to receive(:find).with("1").and_return(team7)
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       allow(team7).to receive(:user_id).with(any_args).and_return(1)
       allow(team7).to receive(:update_attribute).and_return(true)
       session = {user:student1}
       params = {
         student_id:1,
         team_id:1,
         team:{
           name:'test'
         },
         action: 'update'
       }
       result= post :update, params, session
       expect(result.status).to eq(302)
     end
   end
   #When no team has name and only one matching team is found,update the name
   context 'update name when name is found' do
     it 'update name when no team has name and only one matching team is found' do
       allow(AssignmentTeam).to receive(:where).with(name: 'test', parent_id: 1).and_return(team1)
       allow(Team).to receive(:find).with("1").and_return(team8)
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       allow(team8).to receive(:user_id).with(any_args).and_return(1)
       allow(team8).to receive(:update_attribute).and_return(true)
       allow(team1).to receive(:length).and_return(1)
       allow(team1).to receive(:name).and_return("test")
       allow(team8).to receive(:name).and_return("test")
       session = {user:student1}
       params = {
         student_id:1,
         team_id:1,
         team:{
           name:'test'
         },
         action: 'update'
       }
       result= post :update, params, session
       expect(result.status).to eq(302)
     end
   end
   #when the team name is already in use, then flash the error message
   context 'name is already in use' do
     it 'when the team name is already in use flash notice' do
       allow(AssignmentTeam).to receive(:where).with(name: 'test', parent_id: 1).and_return(team1)
       allow(Team).to receive(:find).with("1").and_return(team8)
       allow(AssignmentParticipant).to receive(:find).with('1').and_return(student1)
       allow(AuthorizationHelper).to receive(:current_user_has_id).with(any_args).and_return(true)
       allow(student1).to receive(:user_id).with(any_args).and_return(1)
       allow(team8).to receive(:user_id).with(any_args).and_return(1)
       allow(team1).to receive(:length).and_return(2)
       session = {user:student1}
       params = {
         student_id:1,
         team_id:1,
         team:{
           name:'test'
         },
         action: 'update'
       }
       result= post :update, params, session
       expect(result.status).to eq(302)
     end
   end
 end

Scenario 4: Remove team participants

  1. Find the user who has to be removed.
  2. Remove the user from team.
  3. If the team does not have any more participants, remove the team record from database.
  4. If the assignment has sign up sheet, then add the topic back to topics pool or assign the topic to a new team from waitlist
  5. Remove all the invitations sent and redirect to view_student_teams_path.

describe '#remove_participant' do
   #remove participant from team and remove team if he was the only particilant
   context 'remove team user' do
     it 'remove user' do
       allow(AssignmentParticipant).to receive(:find).and_return(participant)
       allow(TeamsUser).to receive(:where).and_return(team_user1)
       allow(team_user1).to receive(:destroy_all)
       allow(team_user1).to receive_message_chain(:where,:empty?).and_return(false)
       allow_any_instance_of(AssignmentParticipant).to receive(:save).and_return(false)
       session = {user:student1}
       params = {
         team_id:1,
         user_id:1,
         student_id:1,
         team:{
           name:'test'
         }
       }
       result = post :remove_participant, params, session
       expect(result.status).to eq 302
       # expect(result).to redirect_to(view_student_teams_path(:student_id => 1))
     end
   end
 end

Test Pass/ Fail

Coverage

join_team_requests_controller.rb

P = Pending status, D = Denied status, A = Accepted status. To test the controller, run the line:

rspec spec/controllers/join_team_request_controller_spec.rb

Scenario 1: Creating a team request

  1. If the team id is verified along with user id and assignment id, create a new request and change @join_team_request = 'P' .
  2. If error occurs, flash error message.

 describe "POST #create" do
   before(:each) do
     # Stubbing participant to receive an object with id = 1
     allow(Participant).to receive(:find).with("1").and_return(participant)
   end
   context "when resource is not saved!" do
     it "renders new page" do
       allow(JoinTeamRequest).to receive(:new).and_return(invalidrequest)
       params = {participant_id: participant.id, team_id: -2}
       session = {user: student1}
       get :new, params, session
       expect(response).to render_template("new")
     end
   end
   # Testing when the object is being saved to the database
   context "when resource is saved" do
     before(:each) do
       allow(JoinTeamRequest).to receive(:new).and_return(join_team_request2)
       allow(Team).to receive(:find).with("1").and_return(team1)
       allow(Assignment).to receive(:find).with(1).and_return(assignment1)
       allow(Participant).to receive(:where).with(user_id: 1,parent_id: '1').and_return([participant])
       allow(join_team_request2).to receive(:save).and_return(true)
     end
     it "valid response" do
       allow(join_team_request2).to receive(:save).and_return(true)
       params = {
         id: 2,
         join_team_request2: {
           status: 'P'
         },
         team_id: 1,
         assignment_id: 1
       }
       session = {user: student1}
       post :create, params, session
       expect(response.status).to eq 302
       expect(join_team_request2.status).to eq('P')
     end
   end
   context "when it is not created" do
     it "will page for new " do
       allow(join_team_request2).to receive(:save).and_return(false)
       params = {action: 'new'}
       session = {user: student1}
       get :new, params, session
       expect(response.status).to eq 200
     end
   end
 end

Scenario 2: Decline a team request.

  1. After verifying team_user_id, change @join_team_request.status = 'D' .
  2. Redirect to view_student_teams_path
describe "#decline" do
   context "when join team request is declined" do
     before(:each) do
       allow(JoinTeamRequest).to receive(:find).and_return(join_team_request2)
       allow(join_team_request2).to receive(:save).and_return(true)
     end
     it "will change status to 'D'" do
       params = {action: 'decline'}
       session = {user: ta}
       result = get :decline, params, session
       expect(result.status).to eq 302
     end
     it "will redirect to view student teams path" do
       params = {action: 'decline'}
       session = {user: ta}
       result = get :decline, params, session
       expect(result).to redirect_to(view_student_teams_path)
     end
   end
 end

Scenario 3: Check team status.

  1. Since this method is private there is no test case written for it.
  2. If team if full. Print error message "This team is full." .
  3. If team is not empty, print message "You are already a member of this team."

Test Pass/ Fail

All tests, except 1 pass. The test that fails is for the "get index" method. We have identified that there is no route the view/index is going to.

def respond_after(request)
   respond_to do |format|
     format.html
     format.xml { render xml: request }
   end
 end

Coverage


The controller is used to create, update and remove advertisements for partner. The following test cases are written to test the functionality of advertise_partner_controller.rb.

How to run the test?

 rspec spec/controllers/advertise_partner_controller_spec.rb


Scenario 1: Creating new advertisement for partners.

  1. We set the advertise_for_partner : True, comments_for_advertisement[param] when the user wants to set a new advertisement for partners in AssignmentTeam mode.

 #create advertisement by passing team and participant details
 describe "POST #create" do
   context "when advertisement request is valid" do
     it "will create an advertisement" do
       allow(AssignmentTeam).to receive(:find_by).and_return(team1)
       allow(AssignmentParticipant).to receive(:exists?).and_return(true)
       allow(team1).to receive(:assignment).and_return(assignment1)
       allow(team1).to receive(:update_attributes).and_return(true)
       allow(AssignmentParticipant).to receive(:find_by).and_return(participant)
       params  = {
         id: team1.id,
         team_id: team1.id,
       }
       session = {user: ta}
       result = get :create, params, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(view_student_teams_path(:student_id => 1))
     end
   end
 end

Scenario 2: Updating the advertisement.

  1. We set the comments_for_advertisement[param] when the user wants to set a new advertisement for partners in AssignmentTeam model.
  2. Update Unsuccessful: If there is an error during update then the following error message is thrown "An error occurred, and your advertisement was not updated." and edit page is rendered.
  3. Update Successful: If the advertisement is successfully updated in the database, then the "Your advertisement was successfully updated!" message is prompted and the user is redirected to the view_student_teams_path.

 # Update advertisement by passing team and paticipant details
 describe "POST #update" do
   context "when advertisement is updated" do
     it "the advertisement is updated" do
       allow(AssignmentTeam).to receive(:find_by).and_return(team1)
       allow(AssignmentParticipant).to receive(:exists?).and_return(true)
       allow(team1).to receive(:assignment).and_return(assignment1)
       allow(team1).to receive(:update_attributes).and_return(true)
       allow(AssignmentParticipant).to receive(:find_by).and_return(participant)
       params  = {
           id: team1.id,
           team_id: team1.id,
       }
       session = {user: ta}
       result = get :update, params, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(view_student_teams_path(:student_id => 1))
     end
     it "the advertisement is not updated due to error" do
       allow(AssignmentTeam).to receive(:find_by).and_return(team1)
       allow(AssignmentParticipant).to receive(:exists?).and_return(true)
       allow(team1).to receive(:assignment).and_return(assignment1)
       allow(AssignmentParticipant).to receive(:find_by).and_return(participant)
       allow(team1).to receive(:update_attributes).and_raise(StandardError)
       params  = {
           id: team1.id,
           team_id: team1.id,
       }
       session = {user: ta}
       result = get :update, params, session
       expect(flash[:error]).to eq "An error occurred and your advertisement was not updated!"
       expect(result.status).to eq 200
     end
   end
 end

Scenario 3: Remove the advertisement.

  1. We set the advertise_for_partner : False , comments_for_advertisement : nil when the user wants to set a new advertisement for partners in AssignmentTeam model.
  2. Remove Unsuccessful : If there is an error during removing the advertisement then the following error message is thrown "An error occurred and your advertisement was not removed." and the previous page is rendered.
  3. Remove Successful : If the advertisement is successfully removed in the database then the "Your advertisement was successfully removed!" message is prompted and the user is redirected to the view_student_teams_path.

describe "POST #remove" do
   context "when advertisement is removed" do
     it "the advertisement is removed" do
       allow(AssignmentTeam).to receive(:find_by).and_return(team1)
       allow(AssignmentParticipant).to receive(:exists?).and_return(true)
       allow(team1).to receive(:assignment).and_return(assignment1)
       allow(team1).to receive(:update_attributes).and_return(true)
       allow(AssignmentParticipant).to receive(:find_by).and_return(participant)
       params  = {
           id: team1.id,
           team_id: team1.id,
       }
       session = {user: ta}
       result = get :remove, params, session
       expect(result.status).to eq 302
       expect(result).to redirect_to(view_student_teams_path(:student_id => 1))
     end
     it "the advertisement is not removed due to error" do
       allow(AssignmentTeam).to receive(:find_by).and_return(team1)
       allow(AssignmentParticipant).to receive(:exists?).and_return(true)
       allow(team1).to receive(:assignment).and_return(assignment1)
       allow(AssignmentParticipant).to receive(:find_by).and_return(participant)
       allow(team1).to receive(:update_attributes).and_raise(StandardError)
       params  = {
           id: team1.id,
           team_id: team1.id,
       }
       session = {user: ta}
       result = get :remove, params, session
       expect(flash[:error]).to eq "An error occurred and your advertisement was not removed!"
       expect(result).to redirect_to(request.env['HTTP_REFERER'] ? :back : (:root))
     end
   end
 end

Test Pass/Fail

Coverage

Github Issues

We have created github issue for each non working test.
Link to the issues:
https://github.com/expertiza/expertiza/issues/2170
https://github.com/expertiza/expertiza/issues/2168
https://github.com/expertiza/expertiza/issues/2167

Old Page Link

We have created a new page due to a title error at the new one. Link to the old page: https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2021_-_E2126._Testing_-_Team_Related_Files#Description_about_project

Github Coverage

Our Team

Yi Qiu (mentor)
Aaron Mathew (asmathew@ncsu.edu)
Priya Jakhar (pjakhar@ncsu.edu)
Supriya Krishna (sbkrishn@ncsu.edu)
Snehapriyaa Mathiyalaghan (smathiy@ncsu.edu)