A tester in Mars
The story of a solution to the Mars Rover kata by a tester.
I was recently challenged to work on a coding kata. I’ve been away from what I’d call real programming for a long, long time and because of that, my Github account was shamefully empty. The only projects I had there were my current employer’s private projects.
To make it clear, I’m not a tester who hates coding, but in my latest jobs, other skills, less related to producing code, were more in demand.
A trip to Mars
The challenge was to solve the Mars Rover kata in which a rover lands on a grid surface and has to move around according to coordinates given to it. The kata can be as complex as you want it to be, but I tried to stick to the challenge given to me. I didn’t want to build a chair hung by ropes, when I was asked for a swing tire.
So here’s the result of the challenge: tiffi-on-mars. I’ll be talking about the 1.0.0 release, but I believe that soon there will be new versions with more complex requirements implementations. I really had fun doing it ☺.
Tiffi has been sent to Mars to give candies to Martians.
In an agreement with NASA, Tiffi and a robotic rover are going to land on a plateau on Mars. This plateau is curiously rectangular and limited and is represented in a grid (example: 9,9).
The position and location of the rover is represented by a combination of x and y coordinates and a letter representing one of the four cardinal compass points (N, S, E, W). An example position might be 0, 0, N, which means the rover is in the bottom left corner and facing North.
In order to drive Tiffi around Mars, NASA sends a simple string of letters. The possible letters are L, R and M. L and R makes the rover spin 90 degrees left or right respectively, without moving it from its current position. M means move forward one grid point, maintaining the same heading. Any other instruction is ignored. The square directly North from (x, y) is (x, y+1).
This whole story is nice, but it would be better suited for a graphical interface, for which I really don’t have any talent at all. So I decided for a CLI interface that works as follows:
There are 3 strings as input.
1) the size of the plateau
2) the position where the rover has landed
3) the sequence of movements the rover has to perform so that Tiffi can hand out the candies.
The output is Tiffi’s last position and orientation, so she can be collected and brought back to Earth to load up on candy.
? What are the World’s dimensions? 9x9
? What’s Tiffi’s landing position? 0,0,E
? What’s Tiffi’s movement plan? MMLMRM
Tiffi handed out candies and ended at: 3,1,E
When I was officially a programmer, back in 2004, I used to have nice test coverage of my code, but I didn’t achieve it using TDD. I would write the code, manually check it and have it covered the best I could with unit and integration tests. As a tester, I usually encourage my colleagues to use TDD and that has been proven to be an effective way to ensure test coverage and also leads to better design decisions. This time, I not only told someone that, I did it. And hey fellows, it's true! ☺ Now I’ll bitch even more about it.
Directions and Movements
I started by building the tests for the directions. If the rover is heading North and it turns right, it should face East, if it turns left, it should face West. With that in mind, I designed the first movement rules. I made sure to include tests for invalid directions as well as watching my tests fail before assuming they were working. If you haven’t seen them fail at least once, you probably can’t assume they’re correct.
The directions are important because they’ll define if the rover's position should add or subtract a value. After implementing (and testing) them, I started to deal with the movements.
The movements became a new rule configuration in a way that will allow me to easily adapt if in the future I need to deal with ordinal directions (NE, NW, SW, SE) as well.
Errors regarding bad position and bad coordinates are thrown and will interrupt the program's execution. This is because when it comes to this level, if the inputs are bad, it’s certainly a programming error. All those values must be checked in higher levels, in this case, the CLI.
All base rules implemented, it was time to model Tiffi, who could have any other name. So I created the character file. I can imagine Tiffi as a super cute doll with colorful hair and a nice dress (although in Mars she’d probably opt for a basic white astronaut suit ☺), but the thing is, what really matters to this program is that she’s represented by a position and a cardinal direction.
The program's goal is to know Tiffi’s last position. This means that after she makes a move, her position will change. With this knowledge in hand, I implemented the move method.
By that point, I was asking myself whether the Tiffi entity should be aware of the world’s borders, so that when she was at position 0,0 she’d have to know that she couldn’t move South or West.
Genesis — The creation of the world
I concluded that Tiffi shouldn’t be responsible for the world’s rules. I know it might seem obvious, but it wasn’t for me.
The world has the knowledge of all these rules and it’s also responsible for landing Tiffi on a valid coordinate. And by having the responsibility of landing characters, the world will be allowed to have more characters as well (in case Tiffi finds a friend to help her ☺). At this point, I also moved the rules to a config file in which they’d be fairly isolated.
In the beginning I created the world, the character and the movements. Now the game was formless and empty... Ok, time to create the game.
The dark surface
Maybe because it sounded epic to have a dark interface when talking about the creation of the world or maybe because I don’t have any graphics skills, I decided to go with CLI. Other considerations here were that if I was to create a web page I’d also have to host the page and so on. I chose Less is More and Simple is Better.
That’s when inquirer entered this project to ask the user for input:
- What are the World’s dimensions?
- What’s Tiffi’s landing position?
- What’s Tiffi’s movement plan?
Then in the end, Tiffi would have handed out candies and stopped at a certain position.
I chose to validate the inputs with regexes (challenge accepted meme here) and that enforced me as a tester to check any regex validation two, three, four, a million times. I like using a particular site to help me create the regexes, but there are tons of them on the web. As hard as creating a regex may seem, don’t forget to test it against all kinds of input you may think of.
When Tiffi moved, her position changed. But then, Tiffi could have received invalid instructions, which I chose to ignore. That means that if by chance, Tiffi reached the 0,0 position and had to move one position heading South, she’d just stay where she is.
Those movements couldn’t throw an error and stop the game execution because that would be frustrating (and ugly). Those movements couldn’t be performed, otherwise Tiffi would be in a parallel world (fun, but unreasonable). Since I chose to ignore invalid moves, if there were other movements after those, they’d have to be performed anyway. This means that when an invalid movement was performed, it shouldn't break Tiffi’s position.
In Ruby there are methods that can be called with bang (!). When calling a method with bang, it will mutate the object and when using the "same" method without bang it will return a new object. Here's an experiment using irb to explain this better:
:001 > my_name = “SaRaH”
:002 > new_name = my_name.downcase
:003 > new_name
:004 > my_name
:005 > new_name = my_name.downcase!
:006 > new_name
:007 > my_name
And this is the end of this tester’s journey to Mars. Or almost… The challenge has been completed but before I think of it as done, there’s the retrospective of the “Holy sh**” moments.
My newbie errors and lessons
First things, first: CI, code coverage, code review
I should have instrumented the code at the beginning of the project, but I only remembered about it some commits later. The earlier, the better. Having proper tools to warn you about your code quality is very useful.
Learn not only basic Git commands but also OMG Git commands
Sometimes I committed to master (and pushed it :facepalm: ).
Before starting a feature or a fix, create a new branch and move to it. Before commits and pushes, read the branch you’re on when you type git status ☺. And if you made commits to the master, before pushing it you can still reset and place your changes in a proper branch and act like no one has seen the sh** you did (because they actually didn’t). ☺
Squares on squares, instead of squares, rectangles and hexagons on geometrical forms. Isolate your commits.
When committing changes, try to have as little as possible in a unique commit. That way when you stop to read it later, it will be easier to identify what happened there.
Commits that are too large can be impossible to analyze. Take this crappy commit ☹. I do believe I implemented the game's dynamics in this commit, but you can barely see it because of all the inquirer files on it. And imagine that I’d like to remove the inquirer module. To deal with this commit, I have to deal with the inquirer module's addition AND the game's dynamics, which again, are unreadable here.
When adding a new module, make it in a single commit like “Adds inquirer to gather inputs”.
Pairing is super nice!
All the comments above are pretty obvious now and when criticising someone’s work, it’s easy to see it. When I was coding the project, I stumbled blindly onto those mistakes, while as a tester I usually warn the programmers not to trip on those very same mistakes.
If you’re pairing with someone, chances are lower that four eyes wouldn’t catch those in time, preventing them from becoming a real issue.
It’s easier for me to notice the process errors I’ve made, but maybe there are also bad coding choices. Feel free to get in touch and suggest improvements.
Image credits: Nick Della Mora