Storyboard

Why Use Storyboards

Sometimes we might want to see different steps in a workflow or lifetime of an object. Storyboards are a convenience object to help enable that.

ApprovalTests allows us to look at a complete object instead of just pieces of it. Storyboards allow us to track an object through time.

The mechanism to map time to space that storyboards use is very analogous to a comic book, but with each frame vertically after each other so that it works well with the diff tool and shows a progression.

How To Use Storyboards

Here’s a full example using most of the Storyboard features.

The Scenario

For context, this is the .approved. file generated by the source code below:

Game of Life

Initial Frame:
. . . . .
. . . . .
. X X X .
. . . . .
. . . . .


Start game:
. . . . .
. . X . .
. . X . .
. . X . .
. . . . .


Frame #2:
. . . . .
. . . . .
. X X X .
. . . . .
. . . . .


setting alive: *
setting dead: _

Frame #3:
_ _ _ _ _
_ _ * _ _
_ _ * _ _
_ _ * _ _
_ _ _ _ _


Frame #4:
_ _ _ _ _
_ _ _ _ _
_ * * * _
_ _ _ _ _
_ _ _ _ _


Frame #5:
_ _ _ _ _
_ _ * _ _
_ _ * _ _
_ _ * _ _
_ _ _ _ _

(See snippet source)

Example Storyboard Code

which is produced from the following code:

// Create a Storyboard to track a series of changes
ApprovalTests::Storyboard story;

// Create object to track
GameOfLife game = createBlinker();

// Add a title
story.addDescription("Game of Life");

// Capture the starting state
story.addFrame(game.toString());

// Frame1: with title
game = game.advance();
story.addFrame("Start game", game.toString());

// Frame2: default titling
game = game.advance();
story.addFrame(game.toString());

// Change how it renders and note it in the Storyboard
story.addDescriptionWithData("setting alive", game.setAliveCell("*"));
story.addDescriptionWithData("setting dead", game.setDeadCell("_"));

// 3 more frames
for (int i = 0; i < 3; ++i)
{
    game = game.advance();
    story.addFrame(game.toString());
}

// verify storyboard
ApprovalTests::Approvals::verify(story);

(See snippet source)

Making Objects ‘Storyboard Friendly’

A technique commonly used with storyboards is to make functions that would often return ‘void’ return objects that show the changes.

For example: The following is a setter, which would normally return void. Here we have it return the value set, so we can easily capture it in the Storyboard.

std::string setAliveCell(std::string alive)
{
    aliveCharacter = std::move(alive);
    return aliveCharacter;
}

(See snippet source)

This means we can write:

story.addDescriptionWithData("setting alive", game.setAliveCell("*"));

(See snippet source)

instead of

std::string newValue = "*";
game.setAliveCell(newValue);
story.addDescriptionWithData("setting alive", newValue);

(See snippet source)