CSC/ECE 517 Spring 2022 - E2203: Adding tests for courses controller, eula controller
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%.