I would like to write some unit tests for Lua code being used by ConTeXt, and would like to know if anyone else has done this and what may be require in setting up the framework.

The Lua code in particular is being stored in separate files (e.g. numbering.lua), meaning that a test runner (e.g. Busted) can just include them with require.

It may be worth noting (but probably does not matter) that I am using Python's unittest as a test runner with lunatic-python.

The inherent problem is that the globals defined by LuaTeX and ConTeXt are not naturally in Lua. Tables such as context and texio are simply not accessible without a require that imports them from their corresponding files. (Which looks to be tex/texmf-context/tex/context/base/trac-log.lua and ...who knows, respectively).

Has anyone done unit testing of ConTeXt + Lua? What Lua files might one need to include to get the variables? I would expect there to be an entry-point somewhere that would take care of most of the setup.

It looks like the .lua files of relevance would generally be in ./tex/texmf-context/tex/context/base/ but I am just guessing, and since there are around 840 .lua files I would be grateful for any guidance on where to start.

  • ConTeXt can process .lua files, so why not require the unit test library, and run the test files through context?
    – Aditya
    Aug 20, 2014 at 3:49
  • What Lua files might one need to include to get the variables? -- For the context namespace this does make only limited sense because it relies on Luatex builtins from the tex namespace only available during a TeX run. Also, don’t waste your time trying to run Context .lua files on a regular Lua interpreter. They make use of many of the Luatex extensions even outside the TeX run. Basically, in order to use the fundamental Lua libraries you can run your scripts with mtxrun directly, for everything more complicated you’re going to have to run Context itself. Aug 20, 2014 at 5:16
  • 4
    Btw. are you aware of the test suite? You might draw inspiration from their approach. Aug 20, 2014 at 5:19
  • @Aditya: It's a good suggestion that you make. The unit tests are part of a larger collection, which is run through Python. I'd like to avoid subprocesses and reconciling the results with the Python test runner. Calling the tests via the interpreter seems more granular and integrated -- though what you suggest might be the best answer. Aug 20, 2014 at 12:48
  • @phg: Thanks; Got it re. mtxrun. The test suite is awesome - I did not know about that. (I saw it mentioned but the link from ContextGarden was broken). Aug 20, 2014 at 12:52

1 Answer 1


Just following up on the great comments by @Aditya and @phg, I did the following:

I created a test suite using the really cool lunatest framework. It is trivial to set up because one only needs to require('lunatest'), and there is a helpful set of examples in test.lua.

Once I have created my own my_tests.lua I run it with:

context my_tests.lua --purgeall --batchmode --once

My python test runner then checks the return code of the context process (which would be non-zero for failure).

Having experimented with this for some bit, I found it challenging to capture the output of the context and texio and other functions. To solve this, I moved away from having ConTeXt start the lua and am using vanilla lua with stubs like this:

local function stub(name)
  local stubbed = {}
  local calls = {}
  local metatable = {
      __call = function(meta, ...)
        local call = {}
        call.method = nil
        call.args = arg
        table.insert(calls, call)
      __index = function(meta, ...)
        local method = arg[1]
        return function(...)
          local call = {}
          call.method = method
          call.args = arg
          table.insert(calls, call)
   stubbed._calls = calls
   setmetatable(stubbed, metatable)
   _G[name] = stubbed

function M.setup()

Where M.setup is called by the lunatest runner before each test. The result is that I can now test Lua for the calls it would make to texio and context like this (with a bit more, but not too much, setup involved):

lt = require('lunatest')

function suite.test_double_indent()
  lt.assert_len(5, context._calls) 

In the end, because there is nothing special about this setup (i.e. no luatex variables required) it is easy to invoke with the python-lunatic or other interpreter bridge, if so inclined.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .