Why Not Read the Tests First?

There are many situations where you need to read code, from fixing bugs to adding new features. When you need to understand what an unfamiliar class, function, or method does, knowing how to read the tests first can make reading the code much easier.

In this article, we’ll look at how to use this technique with the source code of Ace (Ajax.org Cloud9 Editor), a JavaScript-based code editor (or text editor).

Before Reading the Source Code

Ace is a code editor built around six main classes[1]: Document, EditSession, Editor, VirtualRenderer, Layer, and Mode. Let’s imagine we need to figure out the role of VirtualRenderer.

Suppose all we know at this point is that:

  • Ace is a code editor (text editor).
  • It has six main classes: Document, EditSession, Editor, VirtualRenderer, Layer, and Mode.

With only this background knowledge, there isn’t much we can do before reading the code, apart from guessing what VirtualRenderer does based on its name. Since the browser handles the final rendering, perhaps VirtualRenderer is responsible for preparing everything up to that point?

Trying to Read the Source Code

With that rough idea in mind, let’s start reading the code. We open virtual_renderer.js, which defines the VirtualRenderer class, and find that it’s 2,440 lines long[2]. It’s hard to read through that much code and understand it at a glance. If we start reading from the first line and work our way down, we’ll probably get overwhelmed by the amount of information pretty quickly.

But if our goal is to find out what VirtualRenderer does, reading its source code line by line isn’t the only way to gather useful information and clues.

Try Reading the Tests First

This is where the technique of reading the tests first comes in.

Let’s put virtual_renderer.js aside for now and open virtual_renderer_test.js, which contains the tests for the VirtualRenderer class. This file is 614 lines long[3], so it feels a little less intimidating than the implementation itself.

setUp: function() {
        require("./config").setLoader(function(moduleName, cb) {
            if (moduleName == "ace/ext/error_marker")
                return cb(null, require("./ext/error_marker"));
            if (moduleName == "ace/mode/javascript")
                return cb(null, require("./mode/javascript"));
            throw new Error("module not configured " + moduleName);
        });
        
        if (editor)
            editor.destroy();
        var el = document.createElement("div");

        el.style.left = "20px";
        el.style.top = "30px";
        el.style.width = "300px";
        el.style.height = "100px";
        el.style.position = "fixed";
        document.body.appendChild(el);
        var renderer = new VirtualRenderer(el);
        editor = new Editor(renderer);
        editor.on("destroy", function() {
            el.remove();
        });
    },

Near the beginning of the test file, we find a method called setUp, shown above. It creates a div element (el) using document.createElement, sets style properties such as left, top, width, and height directly, and then passes the element to the VirtualRenderer constructor. The resulting renderer instance is then passed to the Editor constructor to create an editor instance.

This setUp method prepares the environment for running the tests. From this code, we can learn that:

  • VirtualRenderer is created with a DOM element as an argument, and the resulting VirtualRenderer instance is passed to Editor.

We could write down this information as a simple flow showing how the objects are passed along:

DOM element → VirtualRenderer → Editor

This isn’t a formal notation like UML, but that’s fine. Any way of representing the information is useful as long as it helps us understand the code.

Test code often shows the conditions and setup needed to use the class or method being tested. In other words, tests let us look at a class or method from the outside, focusing on its interface and inputs and outputs rather than its internal implementation. This makes them particularly useful when we want to get a general idea of what a class or method does.

In this example, we’ve learned that VirtualRenderer acts as a bridge between the DOM and Editor. You might already be imagining how Editor could use VirtualRenderer to manipulate the DOM through calls such as editor.renderer.doSomething.

Gathering Clues from Test Names

Let’s take another look at the test code.

"test: screen2text the column should be rounded to the next character edge" : function() {
 var renderer = editor.renderer;

 renderer.setPadding(0);
 renderer.setSession(new EditSession("1234"));

 var r = renderer.scroller.getBoundingClientRect();
 function testPixelToText(x, y, row, column) {
  assert.position(renderer.screenToTextCoordinates(x+r.left, y+r.top), row, column);
 }

 testPixelToText(renderer.characterWidth * 0.4, 0, 0, 0);
 testPixelToText(renderer.characterWidth * 0.5, 0, 0, 1);
 testPixelToText(renderer.characterWidth * 0.9, 0, 0, 1);
 testPixelToText(renderer.characterWidth * 1.0, 0, 0, 1);
 testPixelToText(renderer.characterWidth * 1.4, 0, 0, 1);
 testPixelToText(renderer.characterWidth * 1.5, 0, 0, 2);
},

The details vary depending on the testing framework, but tests usually have descriptive names, such as “test: screen2text the column should be rounded to the next character edge”. To get a general idea of what the VirtualRenderer class does, let’s skim through these names without reading the test implementations.

  • “test: screen2text the column should be rounded to the next character edge”
  • “test: handle css transforms”
  • “test pixelposition in surrogate pairs”
  • “test scrollmargin + autosize”
  • “test scrollbars after value change”
  • “test autosize from 0 height”
  • “test autosize with newlines”
  • “test invalid valus of minLines”
  • “test line widgets”
  • “test wrapped text rendering”
  • “test resize”
  • “test placeholder”
  • “test: highlight indent guide”
  • “test annotation marks”
  • “test ghost text”
  • “test multiline ghost text”
  • “test long multiline ghost text”
  • “test: brackets highlighting”
  • “test: scroll cursor into view”
  • “test: set gutter class”
  • “test: screenToTextCoordinates with line widget offset, issue #5874”

The test file contains more than 600 lines of code, but that’s all we need to read if we’re only interested in the test names. Even a quick look at keywords such as handle css transforms, scrollmargin + autosize, and wrapped text rendering gives us an idea of some specific features. It looks like VirtualRenderer handles CSS transforms and adjustments to the display area.

As we can see, test names describe some of the features provided by a class or method. These details can give us useful clues about its role. They may not be as informative as well-written documentation or comments, but they’re a convenient way to gather information quickly.

By looking through the tests, we’ve learned several things about VirtualRenderer with minimal effort: it acts as a bridge between the DOM and Editor, and it has features related to CSS transforms and display area adjustments. When we return to the VirtualRenderer source code, we’ll already have this background knowledge in mind. It should help us decide which parts of the code to focus on and where to start reading.

Final Thoughts

We’ve looked at how reading the tests first can help us gather useful clues in a short time and make code easier to read.

The most important thing is to remember that reading the tests first is an option when we’re faced with unfamiliar code or an overwhelming amount of it, and to be able to change our approach when needed. If we can pick up even a few clues from the tests, we’re less likely to feel completely lost when we return to the source code.

It’s only natural to find code reading difficult when we don’t have enough information or background knowledge to understand the source code. By gathering clues and filling in those gaps, we can make the process of reading and understanding code a little less demanding.


  1. Ace API Documentation ↩︎

  2. As of November 4, 2026. ↩︎

  3. As of November 4, 2026. ↩︎