CSC/ECE 517 Spring 2022 - E2203: Adding tests for courses controller, eula controller

From Expertiza_Wiki
Jump to navigation Jump to search

About Expertiza

Expertiza is the software benefits for both instructors and students by supporting various types of submissions and providing reusable objects for peer review. It is an open-source project based on Ruby on Rails framework. It allows the instructors not only to create and customize new or existing assignments but also to create a list of topics the students can sign up for. Students can form teams to work on various projects and assignments. Expertiza also lets students peer-review other students' submissions, enabling them to work together to improve others' learning experiences.

Description about project

This page is a description of Expertiza OSS project E2203 which is adding unit tests for courses_controller.rb, eula_controller.rb.

Files Involved

courses_controller.rb courses_controller_spec.rb eula_controller.rb eula_controller_spec.rb

Running Tests

  rspec ./spec/controllers/courses_controller_spec.rb
  rspec ./spec/controllers/eula_controller_spec.rb

Test Plan

At the beginning of the project, we first ran the original test cases but it only cover part of the methods in controllers. Therefore, in total, we wrote tests to cover all 4 methods in the eula_controller and added 5 tests to cover all 12 methods in the courses_controller. We created unit test cases with the help of mocked objects (factory objects and stub objects).

Eula Controller Methods

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

  • action_allowed?
  • display
  • accept
  • decline

Courses Controller Methods

The code of the controller can be found here. The methods which miss test cases are:

  • copy
  • view_teaching_assistants
  • add_ta
  • remove_ta
  • set_course_fields

Test Frame for eula controller

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

1. action_allowed? - This is the first action that takes place when the user tries to access the eula. This checks whether the current user is authorized to view the page. It is only available for those with student privileges, and also to make sure students cannot do all actions.

Code snippet:

  describe '#action_allowed?' do

    context 'when current user is student' do
      # first test to make sure student cannot do all actions
      it 'allows all actions' do
        expect(controller.send(:action_allowed?)).to be false
      end
      # then test to make sure student can do accept and decline actions
      it 'allows accept action' do
        controller.params = {action: 'accept'}
        user = student
        stub_current_user(user, user.role.name, user.role)
        expect(controller.send(:action_allowed?)).to be true
      end
      it 'allows decline action' do
        controller.params = {action: 'decline'}
        user = student
        stub_current_user(user, user.role.name, user.role)
        expect(controller.send(:action_allowed?)).to be true
      end
    end
    # make sure action_allowed is only available for those with student privileges
    context 'when current user is anything besides student' do
      it 'allows all actions' do
        user = instructor
        stub_current_user(user, user.role.name, user.role)
        expect(controller.send(:action_allowed?)).to be true
      end
    end
  end

2. display - this action can be called to displays the current page.

Code snippet:

  describe '#display' do

    # check it displays current page
    it 'displays current page' do
      expect(response.status).to eq(200)
    end
  end

3. accept - this action can be called if the is_new_user attribute is updated

Code snippet:

  describe '#accept' do

    # test if the is_new_user attribute is updated
    it 'updates is_new_user attribute' do
      params = {id: 1}
      session = {user: student}
      get :accept, params, session
      expect(session[:user].is_new_user).to eq(false)
    end
    it 'accept redirects to student_task/list' do      
      params = {id: 1}
      session = {user: student}
      post :accept, params, session
      expect(response).to redirect_to('/student_task/list')
    end
  end

4. decline - this action can be called if the message is displayed before the redirect.

Code snippet:

  describe '#decline' do

    # test if the message is displayed before redirect
    it 'displays the flash notice' do
      params = {id: 1}
      session = {user: student}
      get :decline, params, session
      expect(flash[:notice]).to eq 'Please accept the license agreement in order to use the system.'
    end
    # test if it shows the same page again
    it 'redirects to display same page' do
      params = {id: 1}
      session = {user: student}
      get :decline, params, session
      expect(response).to redirect_to('/eula/display')
    end
  end

Test Frame for courses controller

1. copy - this action is to copy the same course from an original existed course, but it can only be done by a different professor.

Code snippet:

  describe '#copy' do
    let(:ccc) { build(:course)}

    context 'when new course id fetches successfully' do
      it 'redirects to the new course' do
        allow(Course).to receive(:find).with('1').and_return(ccc)
        allow(ccc).to receive(:dup).and_return(ccc)
        allow(ccc).to receive(:save!).and_return(true)
        allow(CourseNode).to receive(:get_parent_id).and_return(1)
        allow(CourseNode).to receive(:create).and_return(true)

        params = { id: 1 }
        session = { user: instructor }
        get :copy, params, session
        expect(response).to redirect_to('/courses/edit')
      end
    end
  end

2. view_teaching_assistants - this action is to displays all the teaching assistants for a course.

Code snippet:

  describe '#view_teaching_assistants' do
    let(:course) { double('Course', instructor_id: 6, path: '/cscs', name: 'abc') }
    let(:ta) { build(:teaching_assistant, id: 8) }
    before(:each) do
      allow(Course).to receive(:find).with('1').and_return(course)
      allow(Ta).to receive(:find_all_by_course_id).with('1').and_return([ta])
    end

    it 'should render the view_teaching_assistants page' do
      allow(Course).to receive(:find).with('1').and_return(course)
      allow(course).to receive(:instructor_id).and_return(1)
      params = { id: 1 }
      session = { instructor_id: 1 }
      get :view_teaching_assistants, params, session
      expect(controller.instance_variable_get(:@ta_mappings)).to eq(nil)
    end
  end

3. add_ta - this action is called to add a teaching assistant to a course.

Code snippet:

  describe '#add_ta' do
    let(:user) { build(:student)}
    let(:course) { build(:course)}

    it 'should add a ta to the course' do
      allow(Course).to receive(:find).with('1').and_return(course)
      allow(User).to receive(:find).with('2').and_return(user)
      allow(TaMapping).to receive(:create).and_return(true)
      allow(Role).to receive(:find_by_name).with('Teaching Assistant').and_return(true)
      allow(user).to receive(:save).and_return(true)

      params = { course_id: 1, user: { name: 'Teaching Assistant' } }
      post :add_ta, params
      expect(response).to be_redirect
    end
  end

4. remove_ta - this action is called to remove a teaching assistant from a course.

Code snippet:

  describe '#remove_ta' do
    let(:ta) { build(:student)}
    let(:course) { build(:course)}
    let(:ta_mapping) { build(:ta_mapping)}

    it 'should remove a ta from the course' do
      allow(TaMapping).to receive(:find).with('1').and_return(ta_mapping)
      allow(User).to receive(:find).with('1').and_return(ta)
      allow(ta_mapping).to receive(:destroy).and_return(true)
      allow(Course).to receive(:find).with('1').and_return(course)

      params = { id: 1 }
      post :remove_ta, params
      expect(response).to be_redirect
    end
  end

5. set_course_fields - this action is called in the update and create methods to set the fields of a course.

Code snippet:

  describe '#set_course_fields' do
    it 'should set the course fields' do
      allow(Course).to receive(:find).with('1').and_return(course)
      allow(course).to receive(:set_course_fields).and_return(true)
      params = { id: 1 }
      session = { instructor_id: 1 }
      post :set_course_fields, params, session
      expect(response).to redirect_to('/')
    end
  end

Results

The total coverage of the test is xxx%, 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%.