CSC/ECE 517 Spring 2018/E1813 Test Menu Items Model

From Expertiza_Wiki
Jump to navigation Jump to search

This wiki page is for the description of changes made under E1813 OSS Assignment for Spring 2018, CSC/ECE 517.

Introduction

Expertiza

Expertiza is an open source web application based on Ruby on Rails framework, supported by the National Science Foundation. It is a place where students can submit and peer-review learning objects (assignments, codes, write ups, websites, etc). For an instructor, expertiza allows to create and customize new or existing assignments. For students, it allows to create and work on various projects and assignments. It provides a platform to peer review other students' submissions across various document types, including the URLs and wiki pages.

Test Driven-Development

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: Requirements are turned into very specific test cases, then the software is improved to pass the new tests, only. This is opposed to software development that allows software to be added that is not proven to meet requirements. Test-driven development is related to the test-first programming concepts of extreme programming, begun in 1999,but more recently has created more general interest in its own right. The TDD sequence can be can be summarized in following steps.

  • 1.Add a Test
  • 2.Run all tests and see if the new test fails
  • 3.Write the code
  • 4.Run tests
  • 5. Refactor code
  • 6. Repeat

Advantages of using TDD:

  • Narrowing Problem Focus
  • Tidier Code
  • Not worrying about dependencies
  • Easier refactoring
  • Better Test coverage and fewer bugs

Unit Testing

Unit Testing is a software testing method by which individual units of source code are tested to catch errors early in the development process. For a model it involves testing the interface and on how it responds to commands and queries from outside. Model testing is bounded to the functionality of only the model under test and doesn't test how its collaborating models get affected based on this query.

Unit Testing provides several benefits which can be summarized in the below points.

1. Finds problems early:

Unit testing finds problems early in the development cycle. This includes both bugs in the programmer's implementation and flaws or missing parts of the specification for the unit. In test-driven development (TDD), which is frequently used in both extreme programming and scrum, unit tests are created before the code itself is written. When the tests pass, that code is considered complete.

2. Facilitates change:

Unit testing allows the programmer to refactor code or upgrade system libraries at a later date, and make sure the module still works correctly (e.g., in regression testing). The procedure is to write test cases for all functions and methods so that whenever a change causes a fault, it can be quickly identified. Unit tests detect changes which may break a design contract.

3. Simplifies Integration:

Unit testing may reduce uncertainty in the units themselves and can be used in a bottom-up testing style approach. By testing the parts of a program first and then testing the sum of its parts, integration testing becomes much easier.

4. Documentation:

Developers looking to learn what functionality is provided by a unit, and how to use it, can look at the unit tests to gain a basic understanding of the unit's interface's API.

5. Design:

When software is developed using a test-driven approach, the combination of writing the unit test to specify the interface plus the refactoring activities performed after the test is passing, may take the place of formal design. Each unit test can be seen as a design element specifying classes, methods, and observable behavior.

Problem Statement

This project is to write unit tests using rspec for menu_items.rb model. It is to test all class and instance methods used in this file. The unit tests are to be written to make the path coverage of menu_item.rb more than 90% and achieve the highest possible branch coverage.

Files Involved

The files to be understood and created are:

1. app/models/menu_items.rb

2. spec/models/menu_items_spec.rb

Team Members

Students who collaborated to work on this problem statement are :

1. Harish Pullagurla ( hpullag@ncsu.edu )

2. Kalyan Ghosh (kghosh@ncsu.edu)

3. Sandeep Rajendran(srajend@ncsu.edu)

Plan Of Work - Test Plan

The task in hand was to write test cases for testing the menu_items model file. No Rspec file for the corresponding model exists so there was a need to create a new file and build tests from scratch. For this purposes different sub tasks involved

1. Setting up the Expertiza environment

2. Understand the functionality of model file in menu_items.rb

3. Understand the linked data attributes being used, like controller_actions, content_page, permissions_id

4. Creating dummy entries for testing different functionalities.

5. Writing testing conditions for different functions and cross checking with the expected outputs.

Implementation Steps

Expertiza Environment Setup

The steps that we followed to set up the Expertiza environment are as follows:

1. In the first step we installed the Virtual Box free software from Oracle in our local machines.

2. Then we downloaded the lightweight Lubuntu image file and imported the image file into the virtualbox ubuntu environment.

3. Then we executed the following set up commands in the terminal to set up the application in our local machine.

Setup commands:

  • sudo su
  • gem install bundler
  • exit
  • git clone [Your forked Expertiza repo url]
  • cd expertiza
  • bash setup.sh

(change config/database.yml, there is no MySQL password by default)

  • bundle install
  • rails server


After successfully setting up the environment, we could LogIn to the Expertiza application using our credentials.

To navigate to the menu_item.rb file we performed the following steps.

1. Open terminal inside the virtual environment.

2. Navigate to the model folder of the application by typing the following command in the terminal.

cd/expetiza/app/models

The menu_item_spec.rb test file is present in this directory

expertiza/spec/models/

The menu_item_spec.rb is the main file where test cases have been written.

To navigate to the menu_item.rb file we performed the following steps.

1. Open terminal inside the virtual environment.

2. Navigate to the model folder of the application by typing the following command in the terminal.

cd/expertiza/spec/models

Functionality of Menu Items model

Menu Items is a model which gives the functionality to the top menu bar in the Expertiza website. It controls the display of drop down menus and its sub menus. It directs how these drop downs are displayed with regards to different users which have different permission attributes. A super admin has the permission to edit the menu bar, by adding or deleting menu item blocks from it. Upon adding each item, he gets to position it either in the main menu bar or into different sub categories.

Sample Views

Sample view of menu bar item for instructor ans student are shown below.

View from a student account.

Table

Table corresponding to menu item model has the following attributes:-

1. Name

2. Label :- These attributes talk about the labeling being given to the attributes

3. Parent id :- It tells about under which attribute does this particular attribute fall . If it is set has <null> it is the base category.

4. Sequence id :- It gives sequence numbering for determining the way the attributes are ordered within a parent.

5. controller action id

6. Content page id :- These attributes tell, to which direction/ page, this selection directs the user onto.

Methods in the Model

Different instance methods and class methods exist in this models . Brief description of each of them are : -

1. find_or_create_by_name(params) :- Class Method This method finds or creates a new entry with the given name obtained in params

2. delete :- Instance Method This methods deletes the entry and all the child entries ( entries having the same parent id ) lined to it

3. above :- Instance Method It returns the entry that is above a present sequence number for a given parent id

4. below :- Instance Method It returns the entry that is below a present sequence number for a given parent id

5. repack(repack_id) :- Class Method It modifies the sequence numbers, making them in order, removing the skip entries present in it. It is performed after certain sequences have been deleted. repack_id tells the parent id under which these changes are to be made

6. next_seq(parent id) :- Class Method It returns the next possible sequence id corresponding to a given parent id entries. This function would be helpful if we wish to add a new entry and find out which sequence id is to be given to it.

7. items_for_permission :- Class Method It returns the set of items that are possible to be displayed for a given permission id and also based on controller action id and page id being present for it.

Test Entries Creation

Mock/dummy objects are needed to be created for any unit testing criteria.These objects are loaded freshly and deleted after every testing condition.

Several methods exist for creating such a objects, whose parameters need to be designed to satisfy the conditions under test. Using factories command pattern is one such method where few of the attributes are filled in with predefined values when an created. Here, for this specific case, we haven't used, factories method as, the number of attributes were limited in menu_items model and could be filled in completely with the required values each time.

For testing menu_items, we created required entries into the database using "MenuItem.new()" method, giving different values for each of the test inputs to cover the required testing conditions.

 before(:each) do
   @test1 = MenuItem.new(name: "home1", parent_id: nil, seq: 1, controller_action_id: nil, content_page_id: nil, label: "newlabel")
   @test1.save
 end

The above is an example entry used for creating objects. 6 such test objects were created with entries giving combinations of parent_id and sequence numbers.

Before each test, all the objects are created, which is done using "before(:each)" key word. Also several objects of 'ControllerAction' and 'ContentPage' had to be created for testing one of the methods which acted based on those values.

Testing Conditions

A total of 16 testing conditions were required to be performed for testing all the functions in menu items model file.

The conditions that needed to be tested are as below:

1. .find_or_create_by_name:

In this we had write unit test cases to test if the method returned a menu_item corresponding to a name.
Here we expect that if the method find_or_create_by_name() is passed the parameter "home", then it should return a value equal to "home"

2. #delete:

In this test cases were written to check if the method deletes current menu items and all child menu items.
In this case, if the parent menu item is deleted, all its child record should should be deleted.
For our specific case, the parent and its 3 child menu_items are expected to be deleted.

3. #above:

Test cases were written to check if the below conditions are satisfied:

3.1 "When current menu item has parent_id, the method returns the first parent menu item by querying the parent_id and current sequence number minus one".
     Here we expect that if we invoke the method above() on the active record test4, the method should return a value equal to the active record test3.
3.2 "When current menu item does not have parent_id, the method returns the first parent menu item by querying the current sequence number minus one".
     Here we expect that if we invoke the method above() on the active record test5, the method should return a value equal to the active record test1.

4. #below:

Test cases were written to check if the below conditions are satisfied:

4.1 "When current menu item has parent_id,the method returns the first parent menu item by querying the parent_id and current sequence number plus one".
     Here we expect that if we invoke the method below() on the active record test3, the method should return a value equal to the active record test4
     
4.2 "When current menu item does not have parent_id, the method returns the first parent menu item by querying the current sequence number plus one".
     Here we expect that if we invoke the method below() on the active record test1, the method should return a value equal to the active record test5.

5. .repack:

Test cases were written to check if the below conditions are satisfied:

5.1 "When current menu item has repack_id, the method finds all menus items with parent_id equal to repack_id and repacks the sequence number".
     Here we expect that if the method repack() is invoked on the MenuItem and passed the value 1, 
     then, it will repack the sequence ids and returns the list of repacked seq_ids.
5.2 "When current menu item does not have repack_id, the method finds all menus items with parent_id null and repacks the sequence number".
     Here we expect that if the method repack() is invoked on the MenuItem and passed the value Null, 
     it will match all the values with parent_id = Null and returns the list of repacked seq_ids.

6. .next_seq:

Test cases were written to check if the below conditions are satisfied:

6.1 "When parent_id is bigger than 0, the method selects corresponding menu items with inputted parent_id and returns the next sequence number".
     Here we expect that if we invoke the method next_seq() on the MenuItem and pass the value 1, the method should return a value equal to 5.
     
6.2 "When parent_id is smaller than or equal to 0, the method selects corresponding menu items with parent_id null and returns the next sequence number".
     Here we expect that if we invoke the method next_seq() on the MenuItem and pass the value nil, the method should return a value equal to 6.

7. .items_for_permissions:

Test cases were written to check if the below conditions are satisfied:

7.1 "When inputted variable (permission_ids) is nil and when the controller_action_id of current item is bigger than 0 and when perms does not exist, 
    the method returns the corresponding items".
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems, 
    then the method should return a list of active_records which satisfy the condition.
7.2 "When the controller_action_id of current item is smaller than or equal to 0 and the content_page_id of current item is bigger than 0 and when perms does not exist, 
    the method returns the corresponding items".
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems, 
    then the method should return a list of active_records which satisfy the condition.
7.3 "When the controller_action_id and content_page_id of current item is smaller than or equal to 0, 
    the method returns the corresponding items".
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems, 
    the method should return an empty list.
7.4 "When inputted variable (permission_ids) is not nil and when the controller_action_id of current item is bigger than 0 and when perms exists, 
    the method returns the corresponding items".
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems and passed the value 1 as parameter, 
    then the method should return a list of active_records 
    which satisfy the condition.
7.5 "When the controller_action_id of current item is smaller than or equal to 0 and the content_page_id of current item is bigger than 0, and when perms exists, 
    the method returns corresponding items."
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems and passed the value 1 as parameter, 
    then the method should return a list of active_records which satisfy the condition.
7.6 "When the controller_action_id and content_page_id of current item is smaller than or equal to 0, 
    the method returns the corresponding items."
    Here we expect that if the method items_for_permissions() is invoked on the MenuItems and passed the value 1 as a parameter, 
    the method should return an empty list.

Edge Case Testing

Edge case testing is a kind of testing where we check if the intended functionality works in non-trivial cases.

Examples:

1. .repack::
   In this test case, it is expected that if the method repack() is invoked on the Menu Item and passed the value 1, 
   then, it will repack the sequence ids and returns the list of repacked seq_ids.
   But,we checked the functionality by passing the value -1 to the repack() method, we find that the test case fails as expected.

The screenshot of the actual menu_item_spec.rb file is give below and the pull request can be found here[1]:

Test Runs

A screenshot of the tests passing is shown here

Screencast of a test run can be found here[2]

Conclusion and Learning Outcomes

After writing the test cases we used SimpleCov to measure the C0 coverage of our rails application. In our case we did not have to install SimpleCov explicitly since it was already installed earlier. After running rake spec to run the test cases, SimpleCov creates a directory called coverage in our rails application folder. This folder called coverage contains an index.html file which when opened in a browser renders an html page which gives the C0 coverage of each of the files in the Controllers, Models, Helpers in the app directory.


The learning outcomes after performing this project can be summarized in the following points:

1. We got an hands on experience about how to write tests following the Test Driven Development approach.

2. We learned about how to write units tests for models and controllers in RSpec.

3. We also learned how to understand the functionality of an already developed application.For our case, before writing the test cases for the menu_items.rb we had the understand how different models interacted with each other and how each action by different users would make changes in the database schema.

4. We also gained a better understanding about how different tables, columns of a large applications in structured in the database schema.

References

  1. Expertiza on GitHub
  2. GitHub Project Repository Fork
  3. The live Expertiza website
  4. Expertiza project documentation wiki
  5. Rspec Documentation
  6. Screen Cast of project Running