mlua-lspec 0.2.0

BDD test framework for Lua on mlua — lust-based describe/it/expect with spy/stub/mock
Documentation
  • Coverage
  • 76.19%
    16 out of 21 items documented1 out of 5 items with examples
  • Size
  • Source code size: 125.27 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 683.94 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 33s Average build duration of successful builds.
  • all releases: 49s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • ynishi/mlua-lspec
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • ynishi

mlua-lspec

BDD test framework for Lua on mlua.

Embeds a forked copy of lust and provides Rust APIs for executing Lua tests with structured result collection. Includes Rust-backed spy/stub/mock test doubles and fixture management.

Quick start

let summary = mlua_lspec::run_tests(r#"
    local describe, it, expect = lust.describe, lust.it, lust.expect

    describe('math', function()
        it('adds numbers', function()
            expect(1 + 1).to.equal(2)
        end)
    end)
"#, "@test.lua").unwrap();

assert_eq!(summary.passed, 1);
assert_eq!(summary.failed, 0);

Assertions

-- Equality
expect(value).to.equal(expected)
expect(value).to.equal(expected, epsilon)  -- float tolerance
expect(value).to_not.equal(other)

-- Identity
expect(value).to.be(same_ref)

-- Truthiness / existence
expect(value).to.be.truthy()
expect(value).to.exist()
expect(nil).to_not.exist()

-- Type checking
expect(value).to.be.a('string')
expect(value).to.be.an('number')

-- Comparison
expect(10).to.be.gt(5)     -- greater than
expect(10).to.be.gte(10)   -- greater than or equal
expect(3).to.be.lt(5)      -- less than
expect(3).to.be.lte(3)     -- less than or equal

-- Tables
expect(tbl).to.have(value)            -- contains value
expect(tbl).to.have_key('name')       -- has key
expect(tbl).to.have_length(3)         -- #tbl == 3

-- Strings
expect(str).to.have_length(5)         -- #str == 5
expect(str).to.match('pattern')       -- Lua pattern match

-- Errors
expect(fn).to.fail()
expect(fn).to.fail.with('pattern')

-- Negation (all assertions support to_not)
expect(value).to_not.equal(other)
expect(3).to_not.be.gt(5)
expect(tbl).to_not.have_key('missing')

Test doubles

Spy

Records calls while delegating to the original function.

local s = test_doubles.spy(function(x) return x * 2 end)
s(5)
s:call_count()            -- 1
s:call_args(1)            -- {5}
s:was_called_with(5)      -- true
s:reset()                 -- clear call history

Stub

Returns fixed values without calling any original.

local st = test_doubles.stub()
st:returns(42)
st()                      -- 42

Spy on table method

Replaces a table method with a spy that calls through. Supports revert().

local spy = test_doubles.spy_on(obj, "method_name")
obj.method_name("arg")
spy:call_count()          -- 1
spy:revert()              -- restore original

Mock

Extends spy with declarative expectations verified at test end.

local m = test_doubles.mock(function(x) return x * 2 end)
m:expect_call_count(2)
m:expect_called_with(5)
m(5)
m(10)
m:verify()  -- passes: called 2 times, once with arg 5

Mock methods:

Method Description
mock(fn?) Create mock (optional call-through function)
expect_call_count(n) Expect exactly n calls
expect_called_with(args...) Expect at least one call with these args
expect_never_called() Expect zero calls
verify() Check all expectations (errors if unmet)
clear_expectations() Remove all pending expectations
call_count(), call_args(n), was_called_with(args...) Spy-compatible inspection
returns(val...), reset() Spy-compatible mutation

Fixture

Managed test data store with temporary directory support.

local fix = test_doubles.fixture()

-- Key/value data (any Lua type including tables)
fix:set("user", { name = "alice", age = 30 })
fix:get("user").name      -- "alice"
fix:has("user")            -- true
fix:remove("user")         -- returns the value
fix:count()                -- number of entries
fix:keys()                 -- list of keys

-- File loading
fix:load_file("config", "path/to/config.txt")
fix:get("config")          -- file contents as string

-- Temporary directories (auto-cleaned on GC or cleanup())
local dir = fix:tmpdir("work")
-- dir = "/tmp/lspec-work-12345-0"

-- Explicit cleanup (also happens on GC)
fix:cleanup()

Granular control

For pre-existing Lua VMs (e.g. testing Rust APIs exposed to Lua):

use mlua::prelude::*;

let lua = Lua::new();

// Register your application globals
lua.globals().set("my_api", /* ... */).unwrap();

// Register lspec
mlua_lspec::register(&lua).unwrap();
mlua_lspec::register_doubles(&lua).unwrap();

// Run tests
lua.load(r#"
    local describe, it, expect = lust.describe, lust.it, lust.expect
    describe('my_api', function()
        it('works', function()
            expect(my_api.ping()).to.equal("pong")
        end)
    end)
"#).exec().unwrap();

let summary = mlua_lspec::collect_results(&lua).unwrap();
assert_eq!(summary.failed, 0);

License

MIT OR Apache-2.0