Milestone 3 Requirements
For this milestone, you will be creating an additional class to represent an Order, defining an IOrderItem interface to represent items added to an order, and refactoring your existing classes to implement this interface. You will also be writing unit tests, creating a UML diagram of your Data project, and adding any missing XML-style documentation.
General requirements:
You need to follow the style laid out in the C# Coding Conventions
All classes should be declared in their respective namespace (see below)
Document your classes using XML-Style comments
Create UML class diagrams to represent your project
Assignment requirements:
- Order Class (1)
- IOrderItem Interface (1)
- Refactor existing classes to implement IOrderItem (11-15)
Purpose:
Implementation of interfaces to allow storing multiple types in a generic collection and perform aggregate operations upon them. We will also be practicing writing methods for objects. In addition, we are learning to follow professional documentation practice, both inline and using UML class diagrams. Finally, we are learning to write good tests to verify our programs’ behavior.
Recommendations:
Get in the habit of reading the entire assignment before you start to code. Make sure you understand what is being asked of you. Please do not stub your toe and have to redo work because you did not read the entire assignment.
Accuracy is important. Your class, property, enumeration and other names, along with the descriptions must match the specification given here. Otherwise, your code is not correct. While typos may be a small issue in writing intended for human consumption, in computer code it is a big problem!
Remember that you must document your classes. This includes a general identity comment at the top of your files, i.e.:
/*
* Author: Nathan Bean
* Edited by: (Only include if you are not the original author)
* File name: Something.cs
* Purpose: To inform the students of the requirements for this milestone
*/
Order Class
We now will be creating a class to represent an order. This will need to keep track of all items in the order, and calculate the subtotal price of everything in the order, the tax that would apply to the subtotal, and a total price that is the subtotal and the tax. Additionally, we want to be able to get the calories for the order. Thus, the Order should have the following properties:
DateTime
- a readonly DateTime
which is the date and time the order was taken. This should be set to the current time (i.e. DateTime.Now
) when the Order
class is constructed.
Items
- a readonly array of IOrderItem
, which should be all the items currently in the order.
Subtotal
- a readonly decimal
which is the sum of the prices of all items currently in the order.
Tax
- a readonly decimal
that represents the taxes due on the order, which should be 12% of the Subtotal
Total
- a readonly decimal
which is the sum of the Subtotal
and the Tax
.
Calories
- a readonly uint
which is the sum of all the calories of the items in the order.
In addition to the properties, the Order
class should have the following methods:
Add(IOrderItem item)
- adds a new item to the order. Has no return value.
Remove(IOrderItem item)
- removes the specified item from the order. Returns true if the item was found in the order and successfully removed, otherwise it returns false.
IOrderItem Interface
Define an interface named IOrderItem
in the IOrderItem.cs file and the DogsNSuch
namespace that defines the expected functionality for items added to an order. This should include properties for:
Name
- a readonly string
which is the human-readable name of the item (i.e. "Chicago Dog"
)
Description
- a readonly string
which provides the menu description of the item
Price
- a readonly decimal
that is the price of the item.
Calories
- readonly uint
that is the calories of the item.
Ingredients
- a readonly array of string
which is the list of ingredients for preparing the order item, i.e. ["Hoagie", "Kielbasa", "Sauerkraut", "Brown Mustard"]
.
Refactor Dog, Drink, and Side Classes
To add instances of your dogs, drinks, and side classes to the Order
, you must refactor them to implement the IOrderItem
interface. This will entail adding extra properties to these classes to fulfill the interface requirements.
Refactoring Dog Classes
As your dog classes already have a Name
, Description
, Price
, and Calories
, you will only need to add the Ingredients
property. Its contents should include: the name of the selected bun, the name of selected sausage, and the names of any ingredients whose boolean properties are set to true
. Note that these should be in human case, i.e. “Dill Pickle Slice”, not Pascal or Camel Case.
Hints
A generic
List<T>
can be converted into an array withList<T>.ToArray()
.A
Dictionary<T, U>
can be used to easily map enum values to strings.
Refactoring Drink Classes
Drinks already have properties for Name
, Description
, Price
, and Calories
, so you should only have to add Ingredients
. Since there is nothing to customize on the drinks, this property will always return an empty array.
Refactoring Side Classes
Sides also already have properties for Name
, Description
, Price
, and Calories
, so you should only have to add Ingredients
. Only the Chili has a possible ingredient (cheddar cheese), so the others will always return an empty array.
Unit Testing
You will need to add a unit test class for each dog, side, and drink. These should be defined in the Test
project and DogsNSuch.Test
namespace in a file corresponding to the class it is testing, i.e. ChicagoDog
should have a test class named ChicagoDogUnitTests
declared in the ChicagoDogUnitTests.cs file. You will need to add sufficient test methods to the class to be reasonably sure of its functionality. This includes:
Name Property Tests
You should test that the actual Name
property matches the expected value. These values can be found in the description for Milestone 2. When the Name
property can vary, i.e. with different sizes of side or different flavors of drink, your test should verify the correct name is used based on those other properties, i.e. a large french fry should have the name “Large French Fries” and a Drink with flavor DrinkFlavor.RCCola
should have the name “RC Cola”.
You can either write multiple facts or combine these different configurations under a single theory test.
Description Property Tests
You should test the actual Description
property matches the expected value. These values can be found in the description for Milestone 2.
Price Property Tests
You should test the actual Price
property matches the expected value. These values can be found in the descriptions for Milestone 1 Milestone 2. When the Price
property is calculated (i.e. based on the size of a side), you should test for each possible value. Note you will need to calculate the expected value by hand, and hard-code it into the test.
When the number of possible permutations is very large (i.e. with the Dog
boolean properties), you should test at a minimum:
- Each boolean property in isolation (i.e. each boolean for an ingredient set to
true
while all others arefalse
) - All boolean properties set to
true
- A sampling (at least eight) of different combinations of booleans set to
true
andfalse
Calories Property Tests
You should test the actual Calories
property matches the expected value. These values can be found in the descriptions for Milestone 1 Milestone 2. When the Calories
property is calculated (i.e. based on the size of a side), you should test for each possible value. Note you will need to calculate the expected value by hand, and hard-code it into the test.
When the number of possible permutations is very large (i.e. with the Dog
boolean properties), you should test at a minimum:
- Each boolean property in isolation (i.e. each boolean for an ingredient set to
true
while all others arefalse
) - All boolean properties set to
true
- A sampling (at least eight) of different combinations of booleans set to
true
andfalse
This is most easily accomplished with a theory, though it is possible to use multiple facts.
Enumeration Property Tests
You should test that all enumeration properties are initialized to the correct default value. These expectations can be found in the descriptions for Milestone 1 Milestone 2.
Boolean Property Tests
You should test that all boolean properties are initialized to the correct default value (either true
or false
according to the assignment descriptions). These expectations can be found in the descriptions for Milestone 1 Milestone 2.
Inheritance/Implementation Tests
You should also verify that every class can be treated as its base class (when it inherits from a base class), and that all item classes can be treated as an IOrderItem
. This can be done with the Assert.IsAssignableFrom<T>()
template method, i.e.:
[Fact]
public void ChicagoDogIsADog()
{
var dog = new ChicagoDog();
Assert.IsAssignableFrom<Dog>(dog);
}
[Fact]
public void ChicagoDogIsAnIOrderItem()
{
var dog = new ChicagoDog();
Assert.IsAssignableFrom<IOrderItem>(dog);
}
Collection Tests
For collection properties (like Items
and Ingredients
) you should test that the collection contains the expected values (if any). This can be done with the Assert.Collection()
, Assert.Contains()
, or Assert.Empty()
methods, i.e.:
public void EmptyArrayShouldBeEmpty()
{
string[] empty = new String[0];
Assert.Empty(empty);
}
public void ArrayShouldContainApple()
{
string[] fruits = new String[] {"Apple", "Orange", "Peach"};
Assert.Contains(fruits, "Apple");
}
public void ArrayShouldContainFruits()
{
string[] fruits = new String[] {"Apple", "Orange", "Peach"};
Assert.Collection(fruits,
one => one == "Apple",
two => two == "Orange",
three => three == "Peach"
);
}
Note that Assert.Collection()
tests all the items in the collection, and in a specific order (i.e. the array ["Orange", "Apple", "Peach"]
would fail the third test). Thus multiple Assert.Contains()
calls can be more flexible about ordering:
public void ArrayShouldContainFruitsInAnyOrder()
{
string[] fruits = new String[] {"Apple", "Orange", "Peach"};
Assert.Contains(fruits, f => f == "Apple");
Assert.Contains(fruits, f => f == "Orange");
Assert.Contains(fruits, f => f == "Peach");
Assert.Equals(3, fruits.Length);
}
Because it does not verify the length of the array, we should do that as well (hence the Assert.Equals()
).
As with other calculated fields, you should test different configurations of the class (i.e. different customizations of the Dog
class when testing Dog.Ingredients
, and Order
instances with different IOrderItems
added) to ensure these are being populated correctly. You should have at least eight variations when possible.
Hints
Since the various specialty dog classes inherit the Price
and Calories
from the base Dog
class, you may think that you only need to test these for the base class. Not so! Because your tests will be used to determine if future refactoring introduced breaking changes, you cannot depend on this functionality always being defined in only the base class. So you must duplicate your tests across all of these classes. This is one instance where copying code is acceptable.
XML Style Documentation
All public classes, properties, methods, fields, etc. should be documented inline using UML-Style documentation, as covered in the [documentation chapter]{((<ref 03-documentation>))}.
UML Class Diagram
You will need to include a UML Class Diagram for the Data
project, which should follow the guidelines set out in the UML Chapter. This should be added to a documentation folder in your project, which must be added to source control. See (https://textbooks.cs.ksu.edu/cis400/b-git-and-github/13-adding-documentation-files/) for guidance on ensuring the files are correctly added. You may include either Visio, PDF, or an image file, but including a Visio file ensures you can continue to edit your UML to keep it up-to-date with changes you will make in future milestones.
Submissions
Create a new release tag - Submit the release URL
If you do not remember how to do this, please revisit the Create a Release page
Keep in mind the version!!!