Lua Testing with Busted and Travis-CI

Lua's an awesome language. If you already know what Lua is, skip ahead to the Busted section. If not, here's a brief overview.

Lua is a lightweight (< 500kb), super fast (fastest scripting language in almost all benchmarks when using the Luajit interpreter), simple language. It's primarily used in building game scripts, such as World of Warcraft plugins, but is also used in applications like Adobe Lightroom (which is, or was, largely written in Lua) and Redis's new scripting capabilities. It isn't very opinionated, and I find it to be similar to Javascript in its flexibility; it can be used functionally, OO, or procedurally which provides a wide range of applicability.

It's also, in my opinion, totally underutilized.

Busted

Busted is a simple unit testing framework that styles itself after such test frameworks as Mocha, Jasmine, RSpec, and *Unit. An example might look like this, taken from the official documentation:

require("busted")

describe("Busted unit testing framework", function()
  describe("should be awesome", function()
    it("should be easy to use", function()
      assert.truthy("Yup.")
    end)

    it("should have lots of features", function()
      -- deep check comparisons!
      assert.are.same({ table = "great"}, { table = "great" })

      -- or check by reference!
      assert.are_not.equal({ table = "great"}, { table = "great"})

      assert.true(1 == 1)
      assert.falsy(nil)
      assert.has.error(function() error("Wat") end, "Wat")
    end)

    it("should provide some shortcuts to common functions", function()
      assert.are.unique({ { thing = 1 }, { thing = 2 }, { thing = 3 } })
    end)

    it("should have mocks and spies for functional tests", function()
      local thing = require("thing_module")
      spy.spy_on(thing, "greet")
      thing.greet("Hi!")

      assert.spy(thing.greet).was.called()
      assert.spy(thing.greet).was.called_with("Hi!")
    end)
  end)
end)

Our goal was to make it super easy to read and write; to not have to look up documentation every time you want a test, but at the same time, build in flexibility to cover enough use cases to make it usable by the entire Lua community. So, we provide things like internationalization support (with 6 languages built in at time of writing), definable output types (for human or machine consumption), and composable assertions.

Testing using busted is really easy. The convention I usually follow is:

  • Create a spec folder in my code repository
  • Write module_spec.lua files to hold unit tests, broken up by classes or some other granular unit of functionality
  • Run busted spec from project root.

Installing

Installing busted is easy:

OSX

  • brew install luajit
  • brew install luarocks (luarocks is the "ruby gems" of Lua)
  • luarocks install busted

Linux

  • sudo apt-get install luajit
  • sudo apt-get install luarocks
  • sudo luarocks install busted

Windows

(Forthcoming when I bug my Windows-using friend about it. Install lua, then Luarocks, then luarocks install busted.)

Further Discussion on Busted

The busted docs are fairly solid; I reocmmend you check them out. There's also plenty of information on the internet about testing and TDD and the like. So let's move on to integrating with Travis!

Travis

Travis is a really cool tool that watches changes to Github repositories and automatically runs tests. It's particularly useful because you can check, every time you commit code, if you derped and broke something that you shouldn't have. It has built-in support for many popular languages, such as Ruby, Javascript, and others, but nothing for Lua yet. However, it's pretty easy to abuse a worker meant for another language to get it running busted specs.

Create an account on Travis, then enable Travis to watch your repository. Next, you'll create a .travis.yml file in the root of your project.

In this example .travis.yml file, we'll fake out an erlang worker and run our busted specs using both lua 5.1 and luajit. We define a series of installation steps that will get everything installed and run the script, then post the results to a web hook and send an email to me.

language: erlang

env:
  - LUA=""
  - LUA="luajit"

branches:
  only:
    - master

install:
  - sudo apt-get install luajit
  - sudo apt-get install luarocks
  - sudo luarocks install luafilesystem
  - sudo luarocks install busted

script: "busted spec"

notifications:
  webhooks:
    - http://my-url/travis-results
  recipients:
    - engineering@company.com
  email:
    on_success: change
    on_failure: always

At Olivine Labs, our webhook is actually a hubot endpoint that uses this travis-ci hubot script. This allows our chatbot to display the status in chat every time someone makes a commit or pull request.

-----------------
PASSED on Lua <luassert> [06aad23b79686b6b1293eafcac687658823fd18b]
Compare: https://github.com/Olivine-Labs/luassert/compare/066101917a96...06aad23b7968
Committed by Jack Lawson at 2012-09-06T20:04:34Z
-----------------

Check it out, and feel free to leave feedback and questions!

Posted .