Philip Guo (Phil Guo, Philip J. Guo, Philip Jia Guo, pgbovine)

50 example uses of assertions in my code

Summary
I give 50 examples of assertions (assert statements) from my own software projects to illustrate where and how they can be used to catch coding bugs.

Assertions are a super useful way to catch coding bugs. I like this blurb from Programming with Assertions (emphasis mine):

Each assertion contains a boolean expression that you believe will be true when the assertion executes. If it is not true, the system will throw an error. By verifying that the boolean expression is indeed true, the assertion confirms your assumptions about the behavior of your program, increasing your confidence that the program is free of errors.

Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability.

I've written a lot about assertions in the past:

Professor John Regehr has also covered this topic in depth:

And so have others:

What I've found missing from existing articles is an inventory of examples of how assertions are used in real-world code. I decided to remedy this by grabbing 50 examples from my own code. Why 50? Because I wanted to show a diverse array of use cases but got tired after 50; I wasn't going to make it to 100!

I picked these 50 examples from three of my projects on GitHub:

  1. ruffcut – a prototype Python-based video editor that interfaces with FFmpeg; I use this tool to edit some of my videos.
  2. directory tree inventory – Python scripts for efficiently comparing large directory trees.
  3. Python Tutor – widely-used code visualizer and tutoring environment; despite the name, it's mostly written in JavaScript (actually TypeScript, but same difference).

I chose these projects because they've all seen lots of real-world usage, and I wrote their code so I can explain why each assertion is there. I've shortened some of the example snippets for clarity but linked to the original code. Most of the underlying concepts should generalize to other programming languages.

Here we go!

Where do I place assertions?

I often place my assertions at the beginning of functions (preconditions), right before returning from functions (postconditions), at the top of if-else clauses (control flow invariants), right after complex computations (Pokemon: Gotta Catch 'Em All!), after API function calls, and before an unimplemented feature.

Function Preconditions

These assertions are the most common and easiest to write. After all, whenever you create a new function you should have some preconditions in mind. Or at least I hope you do! So make it a habit to write them as asserts at the top of each function.

Here are three examples from an experimental feature in Python Tutor where you can record video-like demos of your coding sessions. (Note that these are TypeScript method definitions within classes, so they don't begin with the function keyword.)

Before you start recording a video, make sure the app state isn't frozen and TogetherJS (a dependent component) isn't running yet, which matches the comment above this function (full code):

// do this BEFORE TogetherJS gets initialized
startRecording() {
  assert(!this.isFrozen);
  assert(!TogetherJS.running);
  ...

When playing a video demo, make sure TogetherJS is running and the app's frontend UI is in a state for playing the demo (full code):

playFromCurrentFrame() {
  assert(TogetherJS.running && this.frontend.isPlayingDemo);
  ...

This is a callback that's run as soon as the TogetherJS component has finished initializing and is ready to record a demo. Here we check that it's indeed running and that the app's frontend is in recording mode and not in demo playing mode (full code):

// run as soon as TogetherJS is ready in recording mode
recordTogetherJsReady() {
  assert(TogetherJS.running &&
         this.frontend.isRecordingDemo &&
         !this.frontend.isPlayingDemo);
  ...

Note that these are also instances of Application State Checks.

(You shouldn't use assertions to check preconditions of public functions, but whateves all this code is private within my app.)

Function Postconditions

A postcondition asserts something that should be true right before returning out of a function. I write these less often than preconditions, since by the time I finish up a function I'm either too tired or itching to hook it up to its callers to test it out, so I forget to write postcondition asserts. Maybe I should write more!

For instance, at the end of this Python Tutor function, the app's mode should be canonicalized to either 'edit' or 'display'; it might have started in another mode for legacy reasons (full code):

  ...
  assert(this.appMode == 'edit' || this.appMode == 'display');
}

(This is also an instance of Application State Check.)

Control Flow Invariants

I sometimes write asserts at the start of if-else branches to assert what must be true when following that branch.

For instance, if Python Tutor's mode is 'display' here, then this.myVisualizer should be initialized (full code):

// now this.appMode should be canonicalized
// to either 'edit' or 'display'
if (this.appMode === 'edit') {
  ...
} else if (this.appMode === 'display') {
  assert(this.myVisualizer);
  ...
} else {
  assert(false);
}

Also note the assert(false) on the else branch to indicate that app mode should only ever be 'edit' or 'display'.

Here toks can have either 2 or 3 elements, so if it doesn't match the if test for 3 elements, then assert it must have 2 (full code):

toks = s.split(':')
if len(toks) == 3:
    ...
else:
    assert len(toks) == 2
    ...

Here we use the first element of an array obj[0] to indicate the type of data it's holding (yes, fans of stronger type systems will groan here!). If it's of type 'REF', then go down the if branch, but otherwise it must match one of the three C_ types (full code):

if (obj[0] === 'REF') {
  ...
} else {
  assert(obj[0] === 'C_STRUCT' ||
         obj[0] === 'C_ARRAY' ||
         obj[0] === 'C_MULTIDIMENSIONAL_ARRAY');
  ...
}

Similarly, if isCppMode is false (i.e., Python Tutor isn't trying to visualize C/C++ code), then heapObj must be initialized (full code):

if (myViz.isCppMode()) {
  ...
} else {
  assert(heapObj);
}

Finally, this control flow example isn't in an if-else clause, but there's an assert False at the bottom of this function to indicate that it should always return early from the for loop (full code):

def transform_time_with_delta(t):
    for (start_t, end_t, videofile), delta_secs in lst:
        if start_t <= t < end_t:
            return (t + delta_secs, videofile)
    assert False # should never get here!

If this check wasn't here, then Python would simply return None if it ever reaches the end of this function. This might confuse callers that expect an actual return value.

Pokemon: Gotta Catch 'Em All!

Another useful place to write assertions is after my code performs some complex computations, likely right after a loop. It somehow reminds me of Pokemon: Gotta Catch 'Em All! Why? Because I want to make sure everything has been properly accounted for and that I didn't forget to process any elements in my loop.

These examples are harder to illustrate, but here's a simplified one from my directory tree inventory script (full code):

by_filesize = defaultdict(list)
n_records = 0
for line in open(filename):
    record = json.loads(line)
    n_records += 1
    filesize = record['sz']
    by_filesize[filesize].append(record)

assert sum(len(e) for e in by_filesize.values()) == n_records

Here I'm iterating through all lines in a file, processing each as a JSON object (record), extracting the filesize field from each record, and appending it to a list within the by_filesize dictionary in the bucket corresponding to its filesize. I also use n_records to count how many records I've processed so far.

After this loop, I want to make sure I caught 'em all (Pokemon!) by asserting that the sum of the sizes of all lists within by_filesize is equal to n_records. Translated into plain English, this means that all JSON records have been entered into some bucket in by_filesize so that I didn't accidentally miss any of them.

Checking API Calls

Assertions can also check that the return values from API function calls are what you expect. Here I assert that ind is non-negative, which means that inserted_item has been found somewhere in the cur_clips list (full code):

ind = cur_clips.index(inserted_item)
assert ind >= 0

(Note: Even though this idea is still legit, I later discovered that this example is flawed because I thought the Python index() API returns -1 to indicate 'not found', but it actually throws an exception. Whoops, I must've thought it worked like indexOf() in the JavaScript standard library API.)

Marking Unimplemented Features

Finally, a sloppy but convenient use of assertions is to mark unimplemented features. For instance, here in my ruffcut video editor I assert that the app shouldn't be in a special 'star mode' state at this point, since I don't yet support star mode within nested files (full code):

star_mode = parse_yaml_entry(nested_value, output_lst)
# TODO: implement support for stars inside of nested file!
assert not star_mode

Now when I'm running ruffcut and happen to use star mode with a nested file, I'll get this assertion failure instead of continuing onward and seeing a cryptic error elsewhere. If this happens often enough, I might actually consider implementing this feature!

What properties do I assert?

Now that we've covered where I typically place my assertions, here are the kinds of program properties that I assert to be true, starting from the simplest to more complex ones.

Non-Null Object Reference

This is the simplest kind of assert: check that a reference is non-null, which indicates that it has been initialized to some object. (But you still need to trust that the object is the one you want!)

For instance, after the user finishes executing this code in Python Tutor, the app should have created a visualizer object (this.myVisualizer) to display its run-time state (full code):

finishSuccessfulExecution() {
  assert (this.myVisualizer);

When a video demo starts playing, its corresponding audio object should been initialized, or else no audio will play (full code):

startPlayback() {
  ...
  assert(this.mp3AudioRecording); // audio must be initialized

(Note that both of these checks are also function preconditions.)

Numerical Comparison

Whenever you think of a sensible numerical comparison, write it down as an assertion. For instance, in my ruffcut video editor, I often check that video timestamps are ordered chronologically ...

Use timestr_to_secs to convert time strings like '9:34' or '10:29' to seconds and then compare their numerical values (full code):

assert timestr_to_secs(starttime) < timestr_to_secs(endtime)

In Python you can chain multiple comparisons together in one line, which makes for convenient multi-part checks. Here I want to make sure the times to cut from a clip both lie within the bounds of the parent clip and that start is before end (full code):

assert start_secs < cut_start_secs < cut_end_secs < end_secs

For every pair of timestamps b and e (begin and end times) in the clips_in_secs list, check that b occurs before e (full code):

for b, e in clips_in_secs:
    assert b < e

Here's a more obscure Python trick: using zip to pair a list with itself offset by 1 (here, clips_in_secs and clips_in_secs[1:]) allows us to easily grab pairs of neighboring entries (b1, e1 and b2, e2). Now we can check that all pairs of entries within clips_in_secs are ordered chronologically by checking b1 < e1 < b2 < e2 (full code):

for (b1, e1),(b2, e2) in zip(clips_in_secs,clips_in_secs[1:]):
    assert b1 < e1 < b2 < e2

Back to Python Tutor ... here's a y-axis position check for a GUI element to ensure that its arrowOffsetY has been initialized and is between 0 and an initialized codeRowHeight (full code):

assert(this.arrowOffsetY !== undefined);
assert(this.codeRowHeight !== undefined);
assert(0 <= this.arrowOffsetY &&
       this.arrowOffsetY <= this.codeRowHeight);

A simple check (and function precondition!) that the lower and upper bounds of a user-provided range are in order (full code):

playStepRange(lower: number, upper: number) {
  assert(lower <= upper);

String Contents

We often want to check that strings hold certain kinds of valid contents. For instance, here endtime should either be 'end' or a timestamp with a colon in it but with no dash (full code):

# either 'end' or a single timestamp
assert (endtime == 'end' or
        ('-' not in endtime and ':' in endtime))

And this filename should end with an .mp4 extension (full code):

 assert final_output_filename.endswith('.mp4')

You can use regular expressions, but I often find that built-in string API functions are both easier to write and easier to read.

Type Check

Fans of languages with strong static type systems will rightly groan here :) But yes, in dynamically-typed languages like Python and JavaScript, sometimes it's good to explicitly assert the types of objects you're expecting (full code):

for e in lst:
    assert len(e) == 2
    assert type(e[0]) is str  # filename
    assert type(e[1]) is list # editlist for file
    ...

(This kind of hacky code is extremely common in prototypes since it's so fast to develop and so easy to evolve as needs inevitably change. I'd rather throw in some quick asserts like these than create a class hierarchy with type annotations or whatevers.)

Type checks come in handy when you're parsing semi-structured data like JSON or YAML from a file or web API, and you want to assert that some node is of a certain type (e.g., string, list, dictionary). This is much harder to encode in a static type system since you don't know beforehand what your input data will look like. For instance, here I want to make sure that the top-level data structure in this YAML file is a list (full code):

obj = yaml.load(open(yaml_filename), Loader=yaml.FullLoader)
assert type(obj) is list

Data Structure Size

It's also common to check that data structures in your code have a certain size or to compare the sizes of related data structures.

For instance, check that an array isn't empty (full code):

precomputeCurTraceLayouts() {
  assert(this.curTrace && this.curTrace.length > 0);

In my directory tree inventory script, check that the sizes of the moved and not-moved file lists add up to exactly the size of the original file list (full code):

assert len(only_first_files_moved) + \
       len(only_first_files_not_moved) == \
       len(only_first_files)

In ruffcut, check that every dictionary (dict) element in the times_lst list has exactly one item in it (full code):

for e in times_lst:
    if type(e) is dict:
        assert len(e) == 1

Check that the previous video clips list and the current list are the same size, even though they might hold different data (full code):

assert len(self.prev_clips) == len(cur_clips)

Check that seam_times is always exactly 1 element larger than files_to_concat (full code):

assert len(seam_times) == len(files_to_concat) + 1

Tuple Arity

This is related to data structure size but is subtly different. It checks for the arity of a tuple with heterogeneous data (rather than, say, an array/list where each element is of the same type).

For instance, the timerange parameter should be a 3-tuple that holds the start time, end time, and some metadata (full code):

def cache_filename(video_filename, timerange):
    assert len(timerange) == 3

In Python Tutor, if this thoughtfully-named obj object has a tag of C_STRUCT as its first element, then make sure its arity is at least 3 and then extract its 2nd and 3rd elements (full code):

if (obj[0] == 'C_STRUCT') {
  assert(obj.length >= 3);
  var addr = obj[1];
  var typename = obj[2];
  ... access additional elements ...

(Fans of languages with powerful static type systems will cringe at this part, since this could all be encoded into the type system ... but whateves this is Python and JavaScript, YOLO!)

Array Bounds

Check that an integer index is within the bounds of a given array or list. For instance, here we make sure startingInstruction is within the bounds of the this.curTrace array (full code):

assert(0 <= this.parms.startingInstruction &&
       this.parms.startingInstruction < this.curTrace.length);

Here each element in clips_to_highlight is an integer index, so we make sure it's within the bounds of files_to_concat (full code):

for e in clips_to_highlight:
    assert 0 <= e < len(files_to_concat)

Finally, this function precondition checks that the step parameter is within the bounds of this.curTrace (full code):

renderStep(step) {
  assert(0 <= step);
  assert(step < this.curTrace.length);

Application State Check

These assertions check that your application is in a given state, which is usually specified with an enum-style state variable or a set of booleans. Here are a few examples from Python Tutor:

If the app is currently in a playing demo state, stop playing and then check that it's no longer in a playing demo state (full code):

if (this.isPlayingDemo) {
  this.demoVideo.stopPlayback();
  assert(!this.isPlayingDemo);
}

When this function runs, the app should be in a state that is requesting public help and with TogetherJS running (full code):

initRequestPublicHelp() {
  assert(this.wantsPublicHelp);
  assert(TogetherJS.running);

Now when this function runs (to start a private session), the app should not be in a requesting public help state (full code):

initPrivateSharedSession() {
  assert(!this.wantsPublicHelp);

If we're calling a JSONP endpoint, then make sure the language selection (pyState) isn't '2' or '3' (for Python 2 or 3, respectively) ... it should be another language like 'java' or 'ruby' (full code):

if (jsonp_endpoint) {
  assert (pyState !== '2' && pyState !== '3');

If the app is in a recording demo state, stop recording and check that it's no longer recording; otherwise assert it's in a playing demo state since that's the only other valid possibility (full code):

if (this.isRecordingDemo) {
  ...
  this.demoVideo.stopRecording();
  assert(!this.isRecordingDemo);
} else {
  assert(this.isPlayingDemo);
}

Environment State Check

Unlike application state checks, these assertions check the state of the software environment surrounding your app.

My directory tree inventory script requires at least Python 3.5 (or higher) to be installed to use its efficient os.scandir function, so here's my check for that environmental dependency (full code):

# requires python >= 3.5 to get os.walk to use
# the MUCH faster os.scandir function
assert float(sys.version[:3]) >= 3.5

For my ruffcut app, its external FFmpeg dependency doesn't like directories with spaces in names, so check for that (full code):

CACHE_DIR = '~/Desktop/quickrender-cache'
CACHE_DIR = os.path.expanduser(CACHE_DIR)
# ffmpeg doesn't play well with spaces in filenames
assert ' ' not in CACHE_DIR

Before ruffcut runs FFmpeg, make sure the input file videofile_to_use exists on the filesystem; then after it runs FFmpeg with os.system to create an output video file, check that outfile now exists (full code):

assert os.path.isfile(videofile_to_use)
os.system(ffmpeg_cmd)
assert os.path.isfile(outfile)

For web apps like Python Tutor, the environment can be the web browser. Here we check the state of localStorage (full code):

if (!localStorage.getItem('opt_uuid')) {
  localStorage.setItem('opt_uuid', generateUUID());
}
this.userUUID = localStorage.getItem('opt_uuid');
assert(this.userUUID);

Browser DOM elements are also part of the environment of web apps. Here we check that the selected DOM element has a heapObject CSS class (full code):

var dstHeapObject = myViz.domRoot.find('#' + dstID);
assert(dstHeapObject.attr('class') == 'heapObject');

Single Operation Check

It can be useful to check that an operation is performed only once. For instance, here I want to make sure that a particular varDivID value has not yet been placed into the connectionEndpointIDs collection before my code tries to place it in there (full code):

assert(!myViz.jspm.connectionEndpointIDs.has(varDivID));
myViz.jspm.connectionEndpointIDs.set(varDivID, heapObjID);

Why do I need to check this? Because my app should place each varDivID value into the collection exactly once or else something is wrong. If I don't assert this, then this API will happily insert it again and thus hide the bug that's somewhere in my code.

For a similar example in Python, here I'm reading two fields (dn and fn) out of each JSON line in a file and putting them into a records_by_path dictionary. But first I want to make sure that this combination of (dn, fn) isn't already in the dictionary (full code):

for line in open(filename):
    record = json.loads(line)
    dn = record['d']
    fn = record['f']
    assert (dn, fn) not in records_by_path
    records_by_path[(dn, fn)] = record

My script requires each (dn, fn) entry to appear only once in the JSON input file; this assert confirms my assumption.

Finally here I want to make sure pyInputAceEditor has not yet been initialized before calling a function to initialize it, since a double-initialization indicates a bug in my code (full code):

initAceEditor(height: number) {
  assert(!this.pyInputAceEditor);
  this.pyInputAceEditor = ace.edit('codeInputPane');

Data Structure Invariants

The most complex assertions, but also the ones that can find the deepest bugs, are those that enforce invariants about your data structures. (If you write object-oriented code, learn about representation invariants and write checkRep() calls.)

Here's a function that parses a time range string like '1:02-3:45' (full code):

def parse_timerange(s):
    toks = [e.strip() for e in s.split('-')]
    assert len(toks) == 2
    for e in toks:
        assert e in ('start', 'end') or ':' in e
    assert e[0] != 'end'
    assert e[1] != 'start'
    return toks

The invariants of the data structure it returns are that it's a two-element tuple, the first element can't be 'end', the second can't be 'start', and otherwise each is either a timestamp with a ':' or the special 'start' or 'end' value. (I could've used regular expressions here, but this code was way easier for me to write and maintain!)

In this example, if the event field is either exception or uncaught_exception, then the exception_msg field should be non-null so that it can be displayed to the user (full code):

if (curEntry.event === 'exception' ||
    curEntry.event === 'uncaught_exception') {
  assert(curEntry.exception_msg);
  myViz.curLineExceptionMsg = curEntry.exception_msg;
}

Here all the values within the affected_cur_clips_indices list (which represent list indices) should be non-negative (full code):

# check for no negative indices
assert not [e for e in affected_cur_clips_indices if e < 0]

(This one-liner first creates a list containing all negative index values and then asserts that this list is empty.)

Keep this website up and running by making a small donation.

Created: 2019-08-31
Last modified: 2019-09-15
Related pages tagged as programming: