Documentation & Testing
This page lists the milestone requirements for Milestone 2 of the CC 410 Restaurant Project. Read the requirements carefully and discuss any questions with the instructors or TAs.
Purpose
The CC 410 Restaurant Project project for this semester is centered around building a point of sale (POS) system for a fictional restaurant named Hero Pizza, celebrating the heroes from cartoons, comic books, movies, and more.
The second milestone involves writing documentation and unit tests for our existing code base. Our goal is to adequately test each part of our code via unit tests, reaching 100% code coverage at a minimum. In addition, we’ll add all of the required documentation comments in our existing code.
General Requirements
The first couple of milestones only require a subset of the general requirements introduced in the “Hello Real World” project. Read this section carefully to see what is required for this particular milestone.
This milestone must follow these professional coding standards:
- All code must be object-oriented.
- All executable code must be within a class
- Python package files such as
__init__.py
and__main__.py
are exempt.
- Python package files such as
- Classes must be organized into packages based on common usage.
- All executable code must be within a class
- This project must include automation for compilation, unit testing, documentation generation, and execution.
- Java: Use Gradle with the
application
plugin. The project should compile without errors. You may include a main class in a separate package for testing purposes only. - Python: Use tox configured to use Python 3.6 and a requirements file to install libraries. You may include a main class in a separate package for testing purposes only.
- Java: Use Gradle with the
- All code must properly compile or be interpreted.
- Java: It must compile using Gradle.
- Python: It must be interpreted using Python 3.6. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
- Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
- Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
- Python: Use pytest. You may choose to use Hamcrest for assertions.
- Where specified, code should contain appropriate documentation comments following the language’s style guide.
- Java: Use javadoc to generate documentation.
- Python: Use pdoc3 to generate documentation.
- Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.
Assignment Requirements
This milestone should include the following features:
- Each Pizza, Side, and Drink class should contain complete typing information.
- Java - this is already handled by the compiler, so no changes are needed.
- Python - the code should contain complete type annotations and achieve low imprecision percentage in Mypy using strict type checking.
- Each Pizza, Side, and Drink class should have a corresponding class of unit tests that achieve 100% code coverage and adequately test all features of those classes.
- See the discussion below for more information on unit tests to be included.
- Each unit test should be in a matching package in the
test
directory for the class it is testing. - Python - unit tests do not require type annotations.
- Where possible, use parameterized unit tests to reduce the number of individual tests written.
- You may use any form of assertions, including the Hamcrest library.
- Each Pizza, Side, Drink, and Enumeration class should have all required documentation comments.
- Checkstyle/Flake8 should not give any errors related to documentation in the
src
directory. - You are encouraged, but not required, to create documentation comments for unit tests.
- You should be able to generate documentation using javadoc/pdoc3 as shown in the “Hello Real World” project.
- You will be graded on the content of the comments - make sure they are descriptive and succinct, with the appropriate sections/tags.
- Checkstyle/Flake8 should not give any errors related to documentation in the
- Create a UML Class Diagram representing the structure of this program.
- Store the UML diagram as an image file (PNG preferred).
- Place the image in the root of the project directory (directly inside the
java
orpython
folder). - Make sure it is committed to GitHub and included in your project release.
- You may include additional materials, such as the source file used to create the image.
Some quick tips from when I did this milestone:
- DO NOT COPY FROM YOUR SOURCE CODE FROM MILESTONE 1! Write your unit tests solely using the menu on the previous milestone and the list of tests needed on this milestone. In that way, you will confirm that your tests match the specification and confirm the code is correct, not that your tests match your existing code! Even I found a few errors in my code through writing these unit tests.
- You may wish to create global attributes in your unit test classes and then generalize your unit tests. For example, add a global
PRICE = 0.50
attribute, and then use that value in your unit test. In that way, when you copy and paste unit test code, you can simply change the global attributes to match the item being tested. Many tests can be generalized in that way such that all pizza test classes share the same code for many tests, referring to global attributes that are changed in each class. The same works for drinks and sides. - Generalizing the tests for individual ingredients in pizzas and drinks (such as
ham
orcherry
) can be done using reflection or metaprogramming, but I don’t recommend it. Since each ingredient is an individual attribute, generalization is very complex and prone to errors. Those tests were hard-coded for each individual ingredient in my solution. - Java users may wish to review the EnumSource option for parameterized tests using enums.
- Python users can use enums directly in parameterized tests, as in
@pytest.mark.parametrize("crust", Crust)
. - When following Google’s style for Java, you are required to include
default
branches in switch statements across enums, which will be unreached in code coverage. This is fine, but a good reason to avoid switch statements, as you will never get 100% code coverage! I ended up changing my model solution to remove switch statements.
-Russ
Time Requirements
Completing this project is estimated to require 3-8 hours.
In my testing, this milestone requires around 3500-4000 lines of code (including very rudimentary documentation comments) in the unit tests directory. As with the prior milestone, much of the code can be carefully copy-pasted between files with similar attributes. My best suggestion is to pick one of the complex pizzas and start there writing unit tests. Once you have the pizzas all working, the sides and drinks are pretty easy and use much of the same structure. There are several hundred unit tests in my model solution. I ended up finding half a dozen errors in my model solution for milestone 1, showing the importance of unit testing! -Russ
Grading Rubric
This assignment will be graded based on the rubric below:
- Unit Tests - 60%
- Pizza Classes - 30%
- Side Classes - 10%
- Drink Classes - 20%
- Documentation Comments - 30%
- Pizza Classes - 8%
- Side Classes - 8%
- Drink Classes - 8%
- Enumeration Classes - 6%
- UML Class Diagram - 10%
The following deductions apply:
- Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.
This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.
Submission
Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.
Unit Tests
Entrées
Each entrée test class should contain unit tests for the following:
ModificationsInitiallyEmpty()
- theModifications
list should be empty when the object is createdHasCorrectCrustInitially()
- theCrust
attribute is initially set correctlyHasCorrectPriceForCrust(Crust)
- theprice
is correct for each crust optionHasCorrectCalories()
- thecalories
is correctStringIsCorrectForCrust(Crust)
- call thetoString()
or__str__()
method with each type of crust and verify the output.IncludesCorrectVeggiesByDefault(Veggie)
- for each veggie, check if it is included or not by default.- You may modify the arguments to accept a
boolean
value indicating if the veggie should be included by default.
- You may modify the arguments to accept a
AddRemoveVeggies(Veggie)
- for each veggie, check that it can be added and removed, and theVeggies
set will change accordingly.Has<Topping>ByDefault()
- for each topping, check to see that it is included by default (returns true).- For example,
TheMikey
would have a test methodHasHamByDefault()
.
- For example,
Change<Topping>SetsModifications()
- for each topping, check that changing it from and to the default value will add and remove the correct item from theModifications
list.- For example,
TheMikey
would haveChangeHamSetsSpecialInstructions()
that would confirm settingham
tofalse
would add"Hold Ham"
to the list ofModifications
.
- For example,
ChangeMultipleToppingsModifications()
- confirm that changing multiple toppings from their default values will add multiple items to theModifications
list.- This test may be omitted on items that only have one ingredient.
SameObjectsAreEqual()
- generate two different instances of the item, and confirm that they are equal usingequals()
(Java) or==
(Python).DifferentCrustNotEqual()
- generate two different instances of the item using different crust, and confirm that they are not equal usingequals()
(Java) or==
(Python).DifferentToppingsNotEqual()
- generate two different instances of the item using different sets of toppings, and confirm that they are not equal usingequals()
(Java) or==
(Python).DifferentVeggiesNotEqual()
- generate two different instances of the item using different sets of veggies, and confirm that they are not equal usingequals()
(Java) or==
(Python).WrongObjectNotEqual()
- generate an instance of the item and an instance of a different menu item, and confirm that they are not equal usingequals()
(Java) or==
(Python). This should not throw an exception.
Sides
Each side test class should contain unit tests for the following:
DefaultSizeCorrect()
- each side should have the default size ofSmall
when initially created.StringIsCorrectForSize(Size)
- call thetoString()
or__str__()
method with each size and verify the output.HasCorrectPriceForSize(Size)
- theprice
is correct for each sizeHasCorrectCaloriesForSize(Size)
- thecalories
is correct for each sizeSameObjectsAreEqual()
- generate two different instances of the item, and confirm that they are equal usingequals()
(Java) or==
(Python).DifferentSizeNotEqual()
- generate two different instances of the item using different sizes, and confirm that they are not equal usingequals()
(Java) or==
(Python).WrongObjectNotEqual()
- generate an instance of the item and an instance of a different menu item, and confirm that they are not equal usingequals()
(Java) or--
(Python). This should not throw an exception.
Drinks
Each drink test class should contain unit tests for the following:
ModificationsInitiallyEmpty()
- theModifications
list should be empty when the object is createdDefaultSizeCorrect()
- each drink should have the default size ofSmall
when initially created.StringIsCorrectForSize(Size)
- call thetoString()
or__str__()
method with each size and verify the output.HasCorrectPriceForSize(Size)
- theprice
is correct for each sizeHasCorrectCaloriesForSize(Size)
- thecalories
is correct for each sizeHas<Flavor>ByDefault()
- for each flavor included by default, check to see that it is included (returns true). For example,Starfire
would have a test methodHasCherryByDefault()
.DoesNotHave<Flavor>ByDefault()
- for each optional flavor not included by default, check to see that it is not included (returns false).- For example,
Starfire
would have a test methodDoesNotHaveVanillaByDefault()
.
- For example,
Change<Flavor>SetsModifications()
- for each flavor, check that changing it from and to the default value will add and remove the correct item from theModifications
list.- For example,
Starfire
would haveChangeCherrySetsModifications()
that would confirm settingcherry
tofalse
would add"Hold Cherry"
to the list ofModifications
.
- For example,
ChangeMultipleFlavorsModifications()
- confirm that changing multiple flavors from their default values will add multiple items to theModifications
list.- This test may be omitted on items that only have one ingredient.
SameObjectsAreEqual()
- generate two different instances of the item, and confirm that they are equal usingequals()
(Java) or==
(Python).DifferentSizeNotEqual()
- generate two different instances of the item using different sizes, and confirm that they are not equal usingequals()
(Java) or==
(Python).DifferentFlavorsNotEqual()
- generate two different instances of the item using different sets of flavors, and confirm that they are not equal usingequals()
(Java) or==
(Python).WrongObjectNotEqual()
- generate an instance of the item and an instance of a different menu item, and confirm that they are not equal usingequals()
(Java) or--
(Python). This should not throw an exception.
Extra Credit: After writing all of the unit tests listed above, feel free to suggest any unit tests you feel are missing. Email your added tests to the course help email address and you may earn bug bounty points for your suggestions!