Exercise Notes

Learning goals

  • Gain familiarity with and follow software testing frameworks and methodologies
  • Identify and create test scenarios
  • Test code and analyse results to correct errors found using unit testing

Programs used

The ‘V’ Model

There are many different phases to testing and one way of visualising them is using the ‘V’ model below. This describes how the phases of system development tie into testing. For example, business requirements determine the acceptance criteria for User Acceptance Testing (UAT).

V-Model

  1. Can you identify tests at each level in the V-model for a project you’ve worked on? If not, can you imagine what these tests might look like for a well-known software product?
  2. Have you encountered any defects recently? At what stage could a test have been added to detect the defect? Is there now a test to prevent regressions?

Further reading

Table Tennis League

We have an app for keeping track of a league of table tennis players. The app works, but it doesn’t have any automated tests so everyone is afraid they’ll break something when they try to add a new feature. Your job is to add some unit tests to make the app easier to work with.

The league has a pyramid shape, with newbies on the bottom rung and the current winner at the top. There is one player in the top row, two players in the second row, and so on:

Diagram of league pyramid

The bottom row might have gaps because there aren’t enough players to fill it. The rows above it should always be full.

Players can progress up the pyramid by winning a game against a player in the row directly above them. For example, if Dave challenges Cara to a game and Dave wins, they swap places. If Cara wins, they both stay where they are.

League progression

Players can only challenge someone in the row immediately above. So from his initial position, Dave couldn’t challenge Alice, Emma or Grant, for example.

If a new player joins the game, they are added to the bottom row if there’s a space, or to a new bottom row if the current one is full.

Getting started

Clone the repository and open in your IDE. Check that you can run the app by trying a few commands:

  • Add some players
  • Print the state of the league
  • Record the result of a match
  • Find the current league winner
  • Quit the game

The project has a few examples of tests. Run them and make sure they all pass – you should see a message that indicates one test has passed.

Have look through the files to get a rough idea of how the modules fit together. Try sketching the relationship between the source files.

Unit tests

Your goal is write unit tests for the league.js file.

The class has four public methods:

  • addPlayer
  • getRows
  • recordWin
  • getWinner

Think about how you would test each of these, and write tests for them using league_test.js as a starting point. Ignore any other test files for now.

Remember to think about edge cases as well as regular input.

Stretch goals

  • Write unit tests for league_renderer.js using league_renderer_test.js as a starting point.
  • Add a new feature: three strikes rule for players who don’t accept challenges. Make sure you keep running your original tests to check they still pass, as well as adding new tests for the new feature:
    • We need a new command strike Alice Bob which records this result.
    • If Bob challenges Alice but she doesn’t respond, then Alice is given one strike.
    • If anyone challenges Alice again (it could be Bob or a different player) and Alice doesn’t respond, she is given a second strike.
    • When Alice reaches three strikes, the last person who challenged her swaps with her. For example:
      • Alice skips a match with Bob → nothing happens
      • Alice skips a match with Cara → nothing happens
      • Alice skips a match with Dave → Alice and Dave swap places
    • If Alice wins or loses a game against someone, they swap places as normal and her strike count is reset.

Test doubles

Now try out a feature of the game we haven’t covered yet – saved games:

  • Run the app
  • Add some players
  • Save the game to a CSV
  • Quit the game
  • Start a new game
  • Load the game you just saved
  • Check the previous game was loaded
  • Quit the app

Your goal is to write tests for app.js – look at app_test.js for a starting point.

app.js interprets game commands such as add player Alice or print, and uses them to update the game and save or load files.

It has three dependencies:

  • the league
  • the league renderer
  • the file service

Write tests for the following commands:

  • add player Alice
  • record win Alice Bob
  • winner
  • save some/file/path
  • load some/file/path

Decide whether to use stubs, mocks, fakes or the real thing for each of the dependencies. Think about what edge cases to test as well as valid commands.

Stretch goal

The person running the league might forget to save the game before quitting, so let’s add a new auto-save feature which works like this:

  • Whenever the league is updated (by adding a player or recording a win), automatically save the game to a file in a subdirectory called saved_games. You can reuse the file service to do this.
  • There should be one file per game. i.e. a new game should generate a brand new file in saved_games, but updating that game (by adding or swapping players) should overwrite the same file.

You should be able to implement this without changing file_service.js. Remember to add tests using mocks or stubs as appropriate.