Assignment #3: Mars Rover¶
This task puts your unit testing, modules making, and exception handling knowledge to use by writing tests and classes for the Mars rover named Curiosity.
You will create a simulation for issuing commands to Curiosity. The idea is to create a command at mission control, convert that command into a message send it to the rover, then have the rover respond to that message.
We will provide descriptions of the required features you need to implement in three separate classes:
Command
: A type of object containing aCommandType
field.CommandType
is one of the given strings in the table below. SomeCommandTypes
are coupled with aNewPosition
property and some are coupled with aNewMode
property. EveryCommand
object is a single instruction to be delivered to the rover.Message
: AMessage
object has aName
field and can contain severalCommand
objects.Message
is responsible for bundling the commands from mission control and delivering them to the rover.Rover
: An object representing Curiosity, the mars rover. This class contains information on the rover’sPosition
, operatingMode
, andGeneratorWatts
. It will also contain a method that you will write,ReceiveMessage
. This method will include conditional statements for each type of command the rover receives and updates the rover’s properties.
In true TDD form, you will be asked to first write the appropriate units tests for these features, then write the code in the given class to pass those tests.
Getting Started¶
Fork the Mars rover starter code.
Write a unit test for each test item listed below.
Note
Two
Command
object tests have been created for you as examples.Use test-driven development (TDD) to create each of the classes described below.
You are responsible for creating the
Rover.ReceiveMessage()
method.
How-To TDD¶
Recall that in TDD, you write the test for a given behavior before you code the actual function. Feel free to review the Test/Code cycle while you work on this project.
Focus on one test at a time.
Write the test and then create the code to make it pass.
Only write the minimum amount of code needed to make the test pass.
There are some constraints on how you can implement these features. A description of each class is below.
Each numbered item describes a test. You should use the given phrases as the test method names when creating your tests.
You must create 13 tests (14, if you do the bonus) for this assignment.
Warning
Did you catch the part about only working on ONE test at a time? Do NOT try to write all of the tests at once. Doing so will be inefficient and will cause excessive frustration.
A. Command
¶
Command
Class¶
We’ll follow TDD practices for the creation of Message
and Rover
, but for
this class, Command
, we’ve provided the functionality. Command
is already
written for you and you do not need to modify it to write passing tests. Open up and
examine the file Command.cs
.
This class builds an object with three fields:
CommandType
,NewPosition
, andNewMode
.CommandType
is a string that represents the type of command. A command type will be one of the following:'MODE_CHANGE'
or'MOVE'
.To peek ahead at the full functionality of these types, refer to Command Types table.
NewPosition
is anint
value provided in conjunction with a “MOVE” command type.NewMode
is astring
value provided in conjunction with a “MODE_CHANGE” command type.
Example
Command ModeCommand = new Command("MODE_CHANGE", "LOW_POWER");
Command MoveCommand = new Command("MOVE", 12000);
'MODE_CHANGE'
and 'MOVE'
are passed in and set the CommandType
value
by all constructors.
'LOW_POWER'
and 12000 are passed in as value
. Different command
types require different kinds of values and there are two different constructors to
handle each command type and its corresponding value.
Don’t worry about the mode options for now. To peek ahead, see Rover Modes table.
Now that we’ve gone over the class, let’s check out the tests.
Command
Tests¶
To begin, open and examine MarsRoverTests/CommandTests.cs
. Two tests have been created for
you. When a user creates a new Command
object from the class, we want to make
sure they pass a command type as the first argument.
Test 1¶
Note that the test name reads, “ArgumentNullExceptionThrownIfCommandTypeIsNullOrEmpty”.
Look at the constructors in
Command.cs
. In each, a null or emptycommandType
argument results in an exception thrown.Use the “Tests” tab in Visual Studio to run the Command unit tests. Verify that the tests pass.
Next, change the first assertion in
CommandTests.cs
to expectmessage: 'Oops'
. Run the tests again to verify that the test fails (the error message did not match"Command type required."
).Restore the
Assert
method’s expected argument to be"Command type required."
.
Test 2¶
Look at the second Command
test called “ConstructorSetsCommandType”.
This test checks that the constructor in the Command
class correctly sets the CommandType
property in the new object.
Without editing,
Command.cs
contains the correct code. Click “Run” to verify that the first and second tests both pass.You do not need to catch an exception in this test.
You may not need to know the specific types of commands to write this test. In fact, you can change the
commandType
input to any string value and run the test. Does it still pass?
Test 3¶
Look at the third test. “ConstructorSetsInitialNewPositionValue” is the
method name. This test checks that the constructor
correctly sets the NewPosition
field in the new Command
object.
You may not need to know a proper
NewPosition
value in order to write this test.
Run the tests to verify that all 3 command tests pass.
Test 4¶
Write a fourth Command
class test. This should be called “ConstructorSetsInitialNewModeValue”.
This test is responsible for checking that the third field on the Command
class, NewMode
is set by a Command
constructor.
Write the test to check that a
Command
constructor that is passed a second string value will set that string value toNewMode
.Run the test suite. This new test will initially fail.
Add an additional constructor to
Command
that sets theNewMode
field when passed a string value.Re-run the tests. Your new test should pass now.
Note
As you move through the remaining instructions, the amount of guidance will decrease. Refer to your earlier, passing tests to help you construct new tests and passing code.
B. Message
¶
Recall, the role of a message object is to bundle commands to send to the rover.
Message
Class¶
This class builds an object with two properties.
Message(string name, Command[] commands)
Name
is a string that is the name of the message.Commands
is an array ofCommand
objects.
Example
Command[] commands = {new Command("MODE_CHANGE", "LOW_POWER"), new Command("MOVE", 500)};
Message newMessage = new Message("Test message with two commands", commands);
Message
Tests¶
At the same level as CommandTests
, open the test file MessageTests
and
write the unit tests for the Message
class as described below.
Tip
Inside this test file, you will have to create at least one commands
array, fill it with some Command
objects, and pass it into the Message
constructors you are testing.
Test 5¶
This unit test has been started for you. The title, “ArgumentNullExceptionThrownIfNameNotPassedToConstructor”
indicates that it will look similar to the first test in the CommandTests
file.
Review the first test in CommandTests
for an example of how to write this test.
When you run the tests, the test will likely fail because you have not written the class to include this feature.
Look at the code in
Command
. Use that to help you write theMessage
class inMessage.cs
so that your test passes. Refer to the Message Class description above for more details.
Test 6¶
Use “ConstructorSetsName” as the test name. The test confirms
that the constructor in the Message
class correctly sets the
Name
property in a new message object.
Test 7¶
Use “ConstructorSetsCommandsField” as the method name.
This test confirms that the Commands
property of a new message object
contains the data passed in from the Message(name, commands)
call.
Warning
You are moving onto the red planet now. Be prepared for fewer instructions.
C. Rover
¶
Rover
receives a message object, updates its properties from the message, and
returns the results.
Rover Class¶
This class builds a rover object with a few properties. It will also contain
a method called ReceiveMessage
to handle updates to its properties.
public Rover(int position)
position
is a number representing the rover’s position.Sets
Position
toposition
Sets
Mode
to'NORMAL'
Sets default value for
generatorWatts
to 110
public void ReceiveMessage(Message message)
message
is aMessage
objectThis method does not return anything
It applies the contents of the
Message
sent to update certain properties of the rover objectDetails about how to respond to different commands are in the Command Types table.
Example
Command[] commands = {new Command("MOVE", 5000)};
Message newMessage = new Message("Test message with one command", commands);
Rover newRover = new Rover(98382); // Passes 98382 as the rover's position.
Console.WriteLine(newRover.ToString());
newRover.ReceiveMessage(newMessage);
Console.WriteLine(newRover.ToString());
Output
Position: 98382 - Mode: NORMAL - GeneratorWatts: 110
Position: 5000 - Mode: NORMAL - GeneratorWatts: 110
Rover
Tests¶
Open RoverTests.cs
and write the following tests. Write the code to
make them pass in Rover.cs
. Remember to use the given phrase as the test
method name.
Test 8¶
“ConstructorSetsDefaultPosition”. Refer to the Rover Class description above for the default value.
Test 9¶
“ConstructorSetsDefaultMode”.
Test 10¶
“ConstructorSetsDefaultGeneratorWatts”.
Test 11¶
“RespondsCorrectlyToModeChangeCommand”.
The test should check that when a rover object receives a message that contains a “MODE_CHANGE” command, that rover’s
Mode
field is updated.The rover has two modes that can be passed as values to a mode change command, “LOW_POWER” and “NORMAL”.
Test 12¶
“DoesNotMoveInLowPower”.
The test confirms that the rover position does not change when sent a “MOVE” command in “LOW_POWER” mode.
Use the Rover Modes table for guidance on how to handle move commands in different modes.
Test 13¶
“PositionChangesFromMoveCommand”.
A
MOVE
command will update the rover’s position with the position value in the command.
Rover Command Types¶
Command |
Value sent with command |
Updates to |
---|---|---|
MOVE |
Number representing the position the rover should move to. |
|
MODE_CHANGE |
String representing rover mode (see modes) |
|
Note
The rover does not move while in “LOW_POWER” mode.
Rover Modes¶
Mode |
Restrictions |
---|---|
LOW_POWER |
Can’t be moved in this state. |
NORMAL |
None |
Bonus Mission¶
Add the following test that checks for unknown commands in
RoverTests.cs
.
Test 14¶
“RoverReturnsAMessageForAnUnknownCommand”.
Submitting Your Work¶
In Canvas, open the Mars Rover assignment and click the “Submit” button. An input box will appear.
Copy the URL for your repl.it project and paste it into the box, then click “Submit” again.