lunarmodules / luassert

Assertion library for Lua
MIT License
202 stars 77 forks source link

Add support for argument matching #104

Closed o-lim closed 9 years ago

o-lim commented 9 years ago

This allows more flexible argument matching for called_with and returned_with assertions. Argument matching no longer needs to be exact, and can now match against almost any criteria (i.e. types, values, truthy, falsy, patterns, etc.). Custom matchers can be registered via assert:register("matcher", "matcher_name", matcher_func). Furthermore, matchers can be combined with the use of composite matchers.

local assert = require 'luassert'
local match = require 'luassert.match'
local spy = require 'luassert.spy'

local s = spy.new(function() end)
s('foo')
s(1)
s({}, 'foo')
assert.spy(s).was.called_with(match._) -- arg1 is anything
assert.spy(s).was.called_with(match.is_string()) -- arg1 is a string
assert.spy(s).was.called_with(match.is_number()) -- arg1 is a number
assert.spy(s).was.called_with(match.is_not_true()) -- arg1 is not true
assert.spy(s).was.called_with(match.is_table(), match.is_string()) -- arg1 is a table, arg2 is a string
assert.spy(s).was.called_with(match.has_match('.oo')) -- arg1 contains pattern ".oo"
assert.spy(s).was.called_with({}, 'foo') -- you can still match without using matchers

To add your own matcher

local function is_even(state, arguments)
  return function(value)
    return (value % 2) == 0
  end
end

local function is_gt(state, arguments)
  local expected = arguments[1]
  return function(value)
    return value > expected
  end
end

assert:register("matcher", "even", is_even)
assert:register("matcher", "gt", is_gt)
local assert = require 'luassert'
local match = require 'luassert.match'
local spy = require 'luassert.spy'

local s = spy.new(function() end)
s(7)
assert.spy(s).was.called_with(match.is_number()) -- arg1 was a number
assert.spy(s).was.called_with(match.is_not_even()) -- arg1 was not even
assert.spy(s).was.called_with(match.is_gt(5)) -- arg1 was greater than 5

Composite matchers have the form:

match.all_of(m1, m2, ...) -- argument matches all of the matchers m1 to mn
match.any_of(m1, m2, ...) -- argument matches at least one of the matchers m1 to mn
match.none_of(m1, m2, ...) -- argument does not match any of the matchers m1 to mn
ajacksified commented 9 years ago

:+1: Ready to go with a rebase.

I'll version and cut a release of luassert and busted after this.

o-lim commented 9 years ago

Just rebased and submitted a new PR #106 to fix the error level reporting for internal assertions/errors. Could you incorporate #106 into the release as well?

ajacksified commented 9 years ago

Yup.

o-lim commented 9 years ago

I also just updated the README file in PR #107