Testing Example Code In Your Jekyll Posts

??? words · ??? min read

Blocks of code in blog posts and articles often contain errors. I see it frequently, in other people’s writing and my own. Code examples are usually not tested, or even run through a compiler/interpreter, so errors are not surprising.

I’d like my code examples to be as good as the real code that I write. Ideally, all code examples would have tests, which are run before the site is deployed, as part of a CI build process.

And so, I created a Jekyll plugin to enable that workflow.

jekyll-include_snippet

jekyll-include_snippet is a plugin for Jekyll that allows snippets of text to be included from other files. This allows code to be kept separately from the markdown, which makes it easier to test.

The rest of this article will be a short example of how to use the plugin.

The Code

All the code is kept in a single file. In this example, that file is code/dog.rb:

class Dog
  def initialize
    @flipped = false
  end

  # begin-snippet: flip
  def flip
    @flipped = !@flipped
  end
  # end-snippet

  # begin-snippet: speak
  def speak
    @flipped ? "ɟooʍ" : "woof"
  end
  # end-snippet
end

The Markdown

In the YAML frontmatter, specify the path to the code with snippet_source. In the markdown, use the include_snippet liquid tag to include bits of code.

---
title: "Look At My Dog Class"
snippet_source: "code/dog.rb"
---

In this article I'm going to share my `Dog` class with you.

Here is the `flip` method:

```ruby
{% include_snippet flip %}
```

Here is the `speak` method:

```ruby
{% include_snippet speak %}
```

And here is the whole class:

```ruby
{% include_snippet everything %}
```

The markdown above will render the following.

The Rendered Output

In this article I’m going to share my Dog class with you.

Here is the flip method:

def flip
  @flipped = !@flipped
end

Here is the speak method:

def speak
  @flipped ? "ɟooʍ" : "woof"
end

And here is the whole class:

class Dog
  def initialize
    @flipped = false
  end

  def flip
    @flipped = !@flipped
  end

  def speak
    @flipped ? "ɟooʍ" : "woof"
  end
end

The Tests

Testing the code is no different to testing code in any other Ruby project. I’m using RSpec, but you could easily use another framework, like MiniTest.

Add RSpec to your Gemfile:

source 'https://rubygems.org'

gem 'jekyll', '~> 3.7'
gem 'rspec', '~> 3.7'

Install everything with:

bundle install

Generate the conventional RSpec files with:

bundle exec rspec --init

Then write your tests in the spec directory:

# spec/dog_spec.rb

require_relative '../code/dog'

RSpec.describe Dog do
  it 'can woof and flip' do
    expect(subject.speak).to eq("woof")
    subject.flip
    expect(subject.speak).to eq("ɟooʍ")
    subject.flip
    expect(subject.speak).to eq("woof")
  end
end

Finally, all the tests can be run with:

bundle exec rspec

Conclusion

Blog posts and articles often contain incorrect code examples. The jekyll-include_snippet plugin can be used to separate code from markdown, which allows the code to be tested more easily.

The plugin is able to include snippets from more than one file. See the README on GitHub for the documentation.

It’s comforting to know that my example code actually works. This is how I will be writing articles here, from now on. In fact, this article has been written with jekyll-include_snippet, so I’ve got a weird Inception situation happening.


Thank you to Christian Grobmeier for giving feedback on a draft of this article.

Got questions? Comments? Milk?

Shoot an email to [email protected] or hit me up on Twitter (@tom_dalling).

← Previously: Replacing Mocks With Hand-Written Test Doubles

Next up: Dependency Injection Containers vs Hard-coded Constants →

Join The Pigeonhole

Don't miss the next post! Subscribe to Ruby Pigeon mailing list and get the next post sent straight to your inbox.