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.

You should see a couple of simple tests passing. Now actually run the application using Poetry run start 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 that decides 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

We’re using the pytest testing frameworks inside Chessington. pytest is a unit testing framework which contains the following features:

  • Tests can be parametrized which reduces code duplication
  • It does not require separate methods for assertion like: assertEquals, assertTrue, assertContains
  • Allows you to run tests from the console
  • The Pytest community has a rich test of plug-ins to extend the module’s functionality

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.