CSC/ECE 517 Spring 2018 E1813 Menu item tests
This wiki page corresponds to the OSS Project for CSE/ECE 517 (Spring 2018) E1813 Write unit tests for menu_item.rb.
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.
Behavior Driven-Development
In software engineering, behavior-driven development (BDD) is a software development process that emerged from test-driven development (TDD). Behavior-driven development combines the general techniques and principles of TDD with ideas from domain-driven design and object-oriented analysis and design to provide software development and management teams with shared tools and a shared process to collaborate on software development.
BDD focuses on:
1. Where to start in the process
2. What to test and what not to test
3. How much to test in one go
4. What to call the tests
5. How to understand why a test fails
Test-driven development is a software development methodology which essentially states that for each unit of software, a software developer must:
Define a test set for the unit first;
Make the tests fail;
Then implement the unit;
Finally, verify that the implementation of the unit makes the tests succeed.
This definition is rather non-specific in that it allows tests in terms of high-level software requirements, low-level technical details or anything in between. One way of looking at BDD therefore, is that it is a continued development of TDD which makes more specific choices than TDD.
Unit Testing
In computer programming, Unit Testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use. Ideally, each test case is independent from the others. Substitutes such as method stubs, mock objects, fakes, and test harnesses can be used to assist testing a module in isolation.
Some of the advantages of unit testing are:
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. 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. Angad Singh Wadhwa (awadhwa3@ncsu.edu)
2. Bhavuk Jain (bjain@ncsu.edu)
Plan Of Work
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 stub entries for testing different functionalities.
5. Writing testing conditions for different functions and cross checking with the expected outputs.
Implementation Steps
Expertiza Environment Setup
We used the Ubuntu-Expertiza image to setup the environment. We forked the master from Expertiza, cloned that and then run through the command terminal.
cd/expetiza/app/models
The menu_item.rb model file is present in this directory
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.
Parameters
Following are the parameters associated with the menu_item model:
1. Name: This parameter gives the name to the menu item.
2. Label: This parameter gives the label to the menu item.
3. Parent id: It gives the id of the parent and establishes the hierarchy of the various objects of the menu item model. It can also be 'null' which means that it will be the base category.
4. Sequence id: It gives the sequence numbering for determining the way the attributes are ordered within a parent.
5. Controller action id: This parameter is associated with the direction in which the user is directed onto.
6. Content page id: This parameter is also associated with the direction in which the user is directed onto.
Methods
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
Stub 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' 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 "Let" and "MenuItem.create" method, giving different values for each of the test inputs to cover the required testing conditions.
let!(:menuitem) {MenuItem.create id: 1, parent_id: nil, name: "navigate", label: "Navigate", seq: 1, controller_action_id: 1, content_page_id: 1}
The above is an example entry used for menu item. 5 more similar objects were created with entries giving combinations of parent_id, sequence numbers and controller_action_id. We also created objects for controller_action and content_page which were used to define conditions for coverage of items_for_permission.
Testing Conditions
A total of 13 unit tests were performed for testing all the functions in menu items model file and achieving complete code coverage.
The conditions that needed to be tested are as below:
1. .find_or_create_by_name:
In this test case we check whether the find_or_create_by_name function returns a valid menu item.
2. #delete:
For this test case, we delete a parent menu item and check whether the count of total item reduces considering parent and child items are also deleted.
3. #above: Test cases were written to check the below conditions:
3.1 If the given menu item has a valid parent_id, return the previous menu item in terms of sequence number that is a child of the same parent_id. 3.2 If the given menu item has a null parent_id, return the previous menu item in terms of sequence number that also has a null parent_id value.
4. #below: Test cases were written to check the below conditions:
4.1 If the given menu item has a valid parent_id, return the next menu item in terms of sequence number that is a child of the same parent_id. 4.2 If the given menu item has a null parent_id, return the next menu item in terms of sequence number that also has a null parent_id value.
5. .repack: Test cases were written to check the below conditions:
5.1 If the given menu item is updated and parent id is valid, repack is called with current menu item id as repack id and re calculate sequence number for all child items. 5.2 If the given menu item is updated and parent id is null, repack is called with parent id of current menu and sequence number is recalculated for all child items.
6. .next_seq: Test cases were written to check the below conditions:
6.1 If parent id is not null and next_seq is called, the function increments the maximum sequence number for that parent group and returns that value to be stored as the next sequence number for the new menu item. 6.2 If parent id is null, and next_seq is called, the function increments the maximum sequence number amongst all menu items having parent id as null and returns that value to be stored as the next sequence number for the new menu item.
7. .items_for_permissions: Test cases were written to check the below conditions:
7.1 If permission id is null, we check if the controller action id is greater than zero and add menu items to a list accordingly. 7.2 If permission id is not null, we check if controller action id is greater than zero. If not, we check the items for matching content page id and list menu items accordingly.
Conclusion
After writing the test cases we used SimpleCov to measure the C0 coverage of our rails application. 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.
For our case, the C0 coverage of the menu_items.rb file in the Models folder increased from 0 to 100.