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.

Image of Curiosity rover taken by the rover on Mars.

Selfie of Curiosity on Mars.

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:

  1. Command: A type of object containing a CommandType field. CommandType is one of the given strings in the table below. Some CommandTypes are coupled with a NewPosition property and some are coupled with a NewMode property. Every Command object is a single instruction to be delivered to the rover.
  2. Message: A Message object has a Name field and can contain several Command objects. Message is responsible for bundling the commands from mission control and delivering them to the rover.
  3. Rover: An object representing Curiosity, the mars rover. This class contains information on the rover’s Position, operating Mode, and GeneratorWatts. 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

  1. Fork the Mars rover starter code.

  2. Write a unit test for each test item listed below.

    Note

    Two Command object tests have been created for you as examples.

  3. Use test-driven development (TDD) to create each of the classes described below.

  4. 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.

  1. Focus on one test at a time.
  2. Write the test and then create the code to make it pass.
  3. Only write the minimum amount of code needed to make the test pass.
  4. 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.

  1. This class builds an object with three fields: CommandType, NewPosition, and NewMode.
    1. CommandType is a string that represents the type of command. A command type will be one of the following: 'MODE_CHANGE' or 'MOVE'.
      1. To peek ahead at the full functionality of these types, refer to Command Types table.
    2. NewPosition is an int value provided in conjunction with a “MOVE” command type.
    3. NewMode is a string 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”.

  1. Look at the constructors in Command.cs. In each, a null or empty commandType argument results in an exception thrown.
  2. Use the “Tests” tab in Visual Studio to run the Command unit tests. Verify that the tests pass.
  3. Next, change the first assertion in CommandTests.cs to expect message: 'Oops'. Run the tests again to verify that the test fails (the error message did not match "Command type required.").
  4. 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.

  1. Without editing, Command.cs contains the correct code. Click “Run” to verify that the first and second tests both pass.
  2. You do not need to catch an exception in this test.
  3. 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.

  1. 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.

  1. Write the test to check that a Command constructor that is passed a second string value will set that string value to NewMode.
  2. Run the test suite. This new test will initially fail.
  3. Add an additional constructor to Command that sets the NewMode field when passed a string value.
  4. 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

  1. This class builds an object with two properties. Message(string name, Command[] commands)
    1. Name is a string that is the name of the message.
    2. Commands is an array of Command 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.

  1. When you run the tests, the test will likely fail because you have not written the class to include this feature.
  2. Look at the code in Command. Use that to help you write the Message class in Message.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.

  1. public Rover(int position)
    1. position is a number representing the rover’s position.
    2. Sets Position to position
    3. Sets Mode to 'NORMAL'
    4. Sets default value for generatorWatts to 110
  2. public void ReceiveMessage(Message message)
    1. message is a Message object
    2. This method does not return anything
    3. It applies the contents of the Message sent to update certain properties of the rover object
      1. Details 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”.

  1. 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.
  2. The rover has two modes that can be passed as values to a mode change command, “LOW_POWER” and “NORMAL”.

Test 12

“DoesNotMoveInLowPower”.

  1. The test confirms that the rover position does not change when sent a “MOVE” command in “LOW_POWER” mode.
  2. Use the Rover Modes table for guidance on how to handle move commands in different modes.

Test 13

“PositionChangesFromMoveCommand”.

  1. 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 Rover object
MOVE Number representing the position the rover should move to. Position
MODE_CHANGE String representing rover mode (see modes) Mode

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.