Chessington

Learning Goals

  • How to do Test Driven Development (TDD)
  • What unit testing is
  • Useful testing frameworks

Programs Used

With the rise in popularity of chess worldwide it’s time to take advantage of this. Your task is to build a chess application, with the snappy title of Chessington.

Background

Software is a slippery beast. Development sometimes feels like a vigorous game of Whack-a-Rat™, with bugs popping up as swiftly as they are squashed. This exercise is going to teach you some strategies for preventing bugs and keeping quality high.

There are many ways to check that your software is correct. This simplest is to run your program and test every feature to make sure that it’s behaving correctly. The problem comes when you make a change to part of your program. Re-testing your software after every change you make is time-consuming and expensive. It’s also mind-numbingly boring. Developers use Unit Testing as one strategy to reduce the amount of testing required after making changes to an application.

Unit Testing is the practice of writing automated tests which can verify the behaviour of your code. Each unit test should check a single fact about your program. Keeping unit tests small and focused means that when one fails, you know exactly which functionality has broken. A unit test normally contains three parts:

  • Arrange – create the class you’re going to test, and any dependencies.

  • Act – do the thing which you want to check is correct.

  • Assert – check that the right thing happened.

This may sound pretty abstract. It will become clearer once you start working on this exercise.

Setup instructions

You’ll find the code in this repo. Follow the instructions laid out in the README to get the project up and running. Make sure that when you fork the repo, you don’t tick the checkbox for “Copy the main branch only”, as we want to have all branches from the starter repo available for this exercise.

You should see a couple of simple tests passing. Now actually run the application using ./gradlew run start on the command line, you should see a chess board! You can try dragging one of the white pieces to make a move. Except… Oops, none of the logic to decide which moves are valid has been implemented yet. That’s going to be your job! Read on…

The Red-Green-Refactor challenge

Test Driven Development (TDD) is a process for writing unit tests in lock-step with code. Here’s the mantra:

  • Red – We are going to write a test (or tests) which fail(s).
  • Green – We are going to write the simplest code that makes the test pass.
  • Refactor – Once all of our tests pass, we will try to refactor our code to make it beautiful, without breaking any tests.

Writing good unit tests is hard, so we’ve written some for you. This will allow you to get used to the Red-Green-Refactor cycle. By the end of the process, you can start writing your own tests.

Testing framework

You might be wondering what makes a test class different from a normal class, and how your IDE knows how to run them. This is taken care of by a library called JUnit (there are several testing libraries for Java, but JUnit is the most popular and well-known). If a class lives inside the src/test directory and has a name ending in “Test”, JUnit interprets that as a test class. All the methods in it that are annotated with @Test will be interpreted as unit tests and run. JUnit has lots of other annotations and features to help with writing tests, but all we’ll need for now is the basics.

You’ll also have noticed that all the tests end with assertThat(...).... This is the assertion, the part of the test that makes it useful. If we didn’t have this, JUnit would just run the code in the test method to the end and mark it as successful, without actually testing anything! The assertThat lines are how we tell JUnit that the test should fail unless a certain condition holds. assertThat isn’t a part of JUnit, but is from a separate library called AssertJ. Together, JUnit and AssertJ allow us to easily write unit tests with powerful assertions.

You can see more examples of what AssertJ can do here.

Steps to repeat

First, grab some unit tests:

git cherry-pick Red-1

Run your unit tests, and verify that some of them fail. (This is the Red bit.)

Find the failing test(s) in the test runner. Read them, and make sure you understand what they are testing.

Once you understand your test, write the simplest code that makes the test pass.

Once your tests are green, see if you can improve things by refactoring your code to make it nicer (without changing its functionality!). This may not always be possible, but Refactor is an optional step so don’t sweat it.

When you’re happy, and the tests are still green, run the program and try out the new functionality! Then commit your changes with a meaningful comment, and then grab the next set of failing unit tests:

git commit -am "Made pawns move, or something..."
git cherry-pick Red-2

Keep repeating this cycle (increasing the number after Red- each time) until you have something beginning to look like a proper chess game. Try writing the tests for each piece to allow them to take other pieces. You will need to make sure they cannot take the king or their own pieces too!

Writing your own tests

Now it’s your turn to start writing your own tests.

Choose another piece and write a test for it – from looking at the previous tests you should have all the ingredients you need to get started on your own.

Now run your test, see it fail, and make it pass. Continue this (remembering to commit frequently) until you have another piece moving correctly. Continue until all the pieces are done.

While you’re doing this, keep a look out for opportunities for refactoring – this isn’t limited to the game code you’ve written – it might turn out that as your tests grow, there is some common functionality that should be extracted between individual tests, or between the test classes.

Stretch goals

More rules

Chess is a game of difficult edge-cases and weird behaviours. When all of your pieces move correctly, try to implement some of this functionality:

  • En Passant
  • Castling
  • Pawn Promotion
  • Check and Check Mate
  • Stalemate

Don’t forget to write a failing test first!

Scoreboard

Can we keep a record of the score of each player? Try and add functionality to know when a piece has been taken and remember who has taken which pieces so that we can calculate the score of each player.

For now, lets say that

  • A pawn is worth 1
  • Bishops and Knights are worth 3
  • Rooks are worth 5
  • Queens are worth 9

Develop a chess AI

Seriously, how hard can it be? See if you can write a simple AI which plays the Black pieces. Start by picking a valid move at random.