itsfrank/frktest

A basic test framework for Lune

frktest

A basic unit test framework for lune. I developed this mostly for myself, if the project garners interest I will consider renaming it. Otherwise, I plan to use it for my projects until the community consolidates around an alternative (like jest-lua when it gets ported to lune)

Features

  • It runs tests
  • Pretty ergonomic to write (I think so)
  • Assertion library, including table & array checks with diff printing, should_error, and a few more!
  • Builtin console reporter with colors
  • (undocumented) api to implement custom reporters (see lune_console_reporter.luau for sample use)

What does it look like tho?

Here's what a test looks like:

return function()
    test.suite("A collection of tests", function()
        test.case("A test that passes", function()
            local exp = 4
            local got = 2 + 2
            check.equal(got, exp)
        end)

        test.case("A test that fails", function()
            local exp = 3
            local got = 2 + 2
            check.equal(got, exp)
        end)

        test.case("Halt early", function()
            local a = nil
            req.not_nil(a)
            check.falsy(a.b) -- wont run, a failed req stops the test early
        end)

        test.case("Custom messages!", function()
            check.equal(1, 2, msg("my message prints in addition to the expansion"))
        end)

        test.case("Array assertions", function()
            local a = { "a", "b", "c" }
            local b = { "a", "z" }
            check.array.contains(a, { "z", "x" })
            check.array.equal(a, b)
        end)

        test.case("Table assertions", function()
            local a = { a = 1, b = 2, c = { a = 1, b = 2 } }
            local b = { a = 1, b = 22, c = { a = -1 } }
            check.table.contains(a, "b")
            check.table.equal(a, b)
        end)
    end)
end

And here is the output (theme is Rose Pine):

image

How do I use it

Set up your environment

Installing

frktest is available on wally, pesde, or you can just clone the repo.

using wally

Add the dependency:

in wally.toml:

[dev-dependencies]
frktest = "itsfrank/[email protected]"

Create alias in .luaurc:

{
  "aliases": {
    "frktest": "DevPackages/_Index/[email protected]/frktest/src",
  }
}
using pesde

in pesde.toml:

[dev_dependencies]
frktest = { name = "itsfrank/frktest", version = "^0.0.1" }

Create alias in .luaurc:

{
  "aliases": {
    "frktest": "lune_packages/.pesde/itsfrank+frktest/0.0.1/frktest/src/"
  }
}

Note: If you want to use the generated luau file in ./lune_packages, in the examples, replace require("@frktest/frktest") with require("./lune_packages/frktest"). Reporters will be avilable in the _reporters member:

-- from project root
local frktest = require("./lune_packages/frktest")
local lune_console_reporter = frktest._reporters.lune_console_reporter
clone the repo
# somewhere on your machine
git clone https://github.com/itsfrank/frktest.git

Create alias in .luaurc:

{
  "aliases": {
    "frktest": "<path to frktest/src>",
  }
}

All example assume you created a require alias "@frktest" in your project's .luaurc:

{
  "aliases": {
    "frktest": "<see install sections above for paths>",
  }
}

note: you should also register the alias with your lsp of choice, with luau-lsp using VSCode I think it would look like this:

"luau-lsp.require.mode": "relativeToFile",
"luau-lsp.require.directoryAliases": {
    "@frktest/": "<alias in luaurc>"
}

Running tests

The framework does not have a cli or test discovery. So you need to make an entrypoint file, see examples/_run.luau for how I made mine. But basically all you need is this:

-- filename: _run.luau

-- require test files and call the returned function
require("./some_test")()

-- initialize a reporter (there is currently only one... this one, but you can make your own!)
local lune_console_reporter = require("@frktest/reporters/lune_console_reporter")
lune_console_reporter.init()

-- run the tests
local frktest = require("@frktest/frktest")
frktest.run()

Then you can run this file with lune like so:

> lune run _run.luau

Writing tests

I suggest you use these requires (but you do what you want!):

local frktest = require("@frktest/frktest")
local test = frktest.test
local check = frktest.assert.check
local req = frktest.assert.require

A test file should return a function, and for any tests to run the function needs to create tests with test.case. If you want to group tests together you can use test.suite and create tests inside of that. See examples for how I wrote a bunch of test files.

return function()
    test.case("a global test", function()
        -- ...
    end)

    test.suite("a group of tests", function()
        test.case("a test within a suite", function()
            -- ...
        end)
    end)
end

Assertions

Every single assertion has at least one example in the examples folder. If there are any nuances, I added comments.

Really though, they should be self explanatory, your LSP should list them all out if you type assert.check.|

End stuff

TODO

  • Write tests to test the framework (ironic...)
  • Encapsulate test state so that I can test the framework using the framework (with 2 separate states, one doing the testing, and one being tested)
  • Probably add more assertions
  • Fix bugs people might report

Acknowledgements

  • martinfelis/luatablediff for a great table diff inplementation that I adapted for the table.equal assertions
  • kikito/inspect because it's the best pretty printer and I use it to print tables
  • catch2 inspired the test output and probably the assertion syntax

What's up with the name?

My name is Frank, I often use frk as a namespace/prefix for stuff meant for me :)