Giter VIP home page Giter VIP logo

luassert's People

Contributors

ajacksified avatar alerque avatar amsitlab avatar briandcho avatar bullno1 avatar daurnimator avatar deining avatar doriangray avatar elkangaroo avatar jeroendedauw avatar jocelyndelalande avatar johnfoconnor avatar kikito avatar marocchino avatar mindreframer avatar mpeterv avatar nilnor avatar o-lim avatar rafis avatar riskozoslovenska avatar rubensayshi avatar stringtrimmer avatar tbastos avatar tieske avatar trendyne avatar zhangpeihao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

luassert's Issues

Allow custom failure messages

Via email:

In the unit testing frameworks I am familiar with, there is usually
functionality for custom output upon failure of an assertion. I am finding
that with my busted assertions, often I am unsure of what input caused the
failure, as I perform a lot of operations like this:

for i = 1, 19 do
  local truncated = thingy:pulseTruncate( i )
  assert.are.equal( 0, truncated )
end

With the default busted output, I am able to see the invalid return value
that causes the problem, but not which input value caused the problem.
Normally I'd do something like this (pseudocode)

for i = 1, 19 do
  local truncated = thingy:pulseTruncate( i )
  assert.are.equal({
    expect = 0,
    actual = truncated,
    fail = function()
      print("Failed on $i, value of $truncated")
    end
  })
end

Is this possible with busted? I was unable to find functionality like
this in the documentation or by reviewing the code.

a function returning nothing doesn't match is_nil

  it("tests returning nothing as nil", function()
    local f1 = function() return nil end
    local f2 = function() return end
    assert.is_nil(f1())
    assert.is_nil(f2())
  end)

fails on the second assertion, becasue f1 returns 1 argument, nil, f2 returns no arguments.

error message is;

tests returning nothing as nil
spec\state_spec.lua:132: Expected objects to be the same. Passed in:
(nil)
Expected:
type nil

Now the error telling (nil) was passed in is a bug. What should it return?
Should it pass? (nothing == nil) ?
or should the message make clear that no argument was provided?

spy `was_called_with` fails when `self` is touched

I'm a bit new to lua and I'm certain that this probably makes (slightly more) sense to people who do understand the inner workings of lua and luasert, but what I'm running into is inconvenient at best...

when I try to use assert.spy().was_called_with for a method that's bound with :, I need to add the object to the obj to was_called_with as first argument, which makes sense.
but what doesn't make sense is that if the object has been modified between the call and the assert then it fails.

I suspect you have some way to snapshot the arguments being passed in and for a table that means it will think it's a different table being passed in.

this looks like a bug to me, but maybe there's a better way of doing this assertion that doesn't fail, in that case maybe just updating the docs to explain people that would be good enough ;-)

        describe("spies", function()
            it("replaces an original function", function()
                local t = {
                    defaultmsg = "hiii",
                    somerandomproperty = true,
                }

                function t:greet(msg) if msg then print(msg) else print(self.defaultmsg) end end

                local s = spy.on(t, "greet")

                t:greet("Hey!") -- prints 'Hey!'
                assert.spy(t.greet).was_called_with(t, "Hey!")

                t:greet("Hey!") -- prints 'Hey!'

                -- when touching t the next assert fails
                t.somerandomproperty = false
                assert.spy(t.greet).was_called_with(t, "Hey!")
            end)
        end)

it also similarly fails if the mutation it done by the method called, which was ofc my original problem, the above was trying to isolate the problem;

        describe("spies", function()
            it("replaces an original function", function()
                local t = {
                    cnt = 0,
                }

                function t:greet(msg)
                    self.cnt = self.cnt + 1
                    print(msg)
                end

                local s = spy.on(t, "greet")

                t:greet("Hey!") -- prints 'Hey!'
                -- this assert will fail because `t` was touched 
                assert.spy(s).was_called_with(t, "Hey!")
            end)
        end)

PS. <3 busted, a light in the dark when learning lua

Confusing error message for was_not.called

describe("foo", function()
    it("bar", function()
        local func = spy.new(function() end);
        func();
        assert.spy(func).was_not.called();
    end)
end)
-
0 successes / 1 failure / 0 errors / 0 pending : 0.0 seconds

Failure  .\tests\foobar_spec.lua @ 2
foo bar
.\tests\foobar_spec.lua:5: Expected not to be called exactly nil time(s), but it was.

Use of getmetatable in util.lua

When asserting on a table whose metatable has the key __metatable, util.lua will result in an error.
Example:

local troubleSomeTable = {}
setmetatable(troubleSomeTable, {__metatable = 0})
assert.are.same(troubleSomeTable, troubleSomeTable)

It will throw the error: /usr/share/lua/5.1/luassert/util.lua:11: attempt to index local 'mt1' (a number value)

To fix it, getmetatable in util.lua needs to be changed into debug.getmetatable

How to make spy (or mock or stub) not change function type?

Hi!

When wrapping a function in Spy, the type of the resulting entity changes to table. Is it possible to preserve the original type of function when using Spy?
Same behavior when using Mock or Stub

example:

local t = {
    some = function () return 42 end
}

local m = mock(t)

assert.is.Function(m.some) -- raise 'type different types'

I can´t work with luassert:

I'm probably missing something but I can´t see what.
I have downloaded and installed luassert.
I just have an error message:
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require("luassert")
> assert.is.true(true)
stdin:1: '< name >' expected near 'true'

Incorrect crumbs for subtables with shared keys

local assert = require "luassert"

assert.same(
   {{k = "v1"}, {k = "v2"}},
   {{k = "v1"}, {k = "v3"}}
)

currently produces

lua: test.lua:3: Expected objects to be the same.
Passed in:
(table) {
  [1] = {
   *[k] = 'v1' }
 *[2] = {
   *[k] = 'v3' } }
Expected:
(table) {
  [1] = {
   *[k] = 'v1' }
 *[2] = {
   *[k] = 'v2' } }

Note the incorrect extra asterisks for [1][k]. They disappear if the keys in the subtables are different.

Sample has_property registration code has empty error messages

I have tried the code in the README file, with the addition of a namespace, as per #10:

local assert = require "luassert"
local s = require("say") --our i18n lib, installed through luarocks, included as a luassert dependency

local function has_property(table, prop)
  for _, value in pairs(table) do
    if value == prop then
      return true
    end
  end
  return false, {prop, table}
end

s:set("en", "assertion.has_property.positive", "Expected property %s in:\n%s")
s:set("en", "assertion.has_property.negative", "Expected property %s to not be in:\n%s")
assert:register("assertion", "has_property", has_property, "assertion.has_property.positive", "assertion.has_property.negative")

assert.has_property({ name = "jack" }, "name") -- this should "pass"
assert.has_property({ name = "jack" }, "foo") -- this should "fail"

Unfortunately the error message I get when executing this is empty:

➜  lua  lua test_has_property_assertion.lua 
lua: /usr/local/share/lua/5.1/luassert/assert.lua:12: 
stack traceback:
    [C]: in function 'error'
    /usr/local/share/lua/5.1/luassert/assert.lua:12: in function 'has_property'
    test_has_property_assertion.lua:18: in main chunk
    [C]: ?

I expected to see the message "Expected property foo in table #..." somewhere on that text, but it was not.

assert.same gives inconsistent results when given table arguments

I'd argue that the check that assert.same does for exact type equivalence for table values is unwarranted and results in some unexpected results.

Take the following LuaJIT code. 0ULL (of type cdata) and 0 (of type number) compare equal, so I'd expect both of the following assertions to pass:

assert.same(0ULL, 0)
assert.same({0ULL}, {0})

In fact, luassert accepts the first and rejects the second.

Type introspection on spies?

It doesn't seem like it's possible to test code that branches on a condition like type(x) == "function".

For example, the snippet below tests a function example that branches on that condition:

local function example(arg)
    if type(arg) == "function" then
        return arg()
    end
end

describe("example", function()
    it("should handle function arguments", function()
        local spy = spy.new(function() end)

        example(spy)

        assert.spy(spy).was.called()
    end)
end)

Since the type() of a spy is actually "table", the test fails with the following output on the spy assertion:

Failure → spec/example_spec.lua @ 8
example should handle function arguments
spec/example_spec.lua:12: Expected to be called >0 time(s), but was called 0 time(s)

Attempts to mock the global type function to "correctly" return "function" on spied functions also fail with the validation error thrown here.

Is there a way to bypass the validation error, or is there a better way to go about testing the example function?

Feature request: parameter to only show differences between tables

So I'm turning a real-world bug into a busted test right now, where my input data is a large 2d array. Naively using assert.are.same() leads to a silly amount of output, effectively printing the same giant array twice, in full. I'm settling on rewriting the test like so

for y, row in ipairs(expected) do
  for x, cell in ipairs(row) do
    print(x, y)
    assert.are.same(input[y][x], cell)
  end
end

but this is obviously clunky. Since all I need are the differences between each table, it'd be nice to instead have a parameter that just says "elide all unchanged fields below depth N", in the same way deeply nested tables can be elided.

Readme does not explain what the 's' variable is

The code sample on the readme includes the following two lines:

s:set("en", "assertion.has_property.positive", "Expected property %s in:\n%s")
s:set("en", "assertion.has_property.negative", "Expected property %s to not be in:\n%s")

Both lines use a variable called 's', which is never initialized or explained.

I would volunteer a pull request, but I'm afraid I honestly don't know what is it supposed to be. Is it the "say" lib, perhaps?

Mock/Stub non-function values

Imagine we have a table City with the key path City.current.attributes.id = 10

and a function

function isMyCity(my_city_id)
    return City.current.attributes.id == my_city_id
end

It would be useful to mock the value of City.current.attributes.id to test isMyCity. However luassert only supports mocking for keys which contain functions. Why not allow mocking for keys that contain other values. Such as the number in our above example.

Looking at the implementation i dont see a reason for this restriction. Is there something im not aware of preventing this?

Error checking with varargs

In my projects I like to write multiple errors in functions, especially for validating arguments. Currently there's no way to pass arguments into a function called with assert.has.error so it is not possible to test each error message. I would have submitted a pull request, but after reviewing the Guidelines for Contributing I thought I should check with the direction of the project before assuming this is something you would want to include.

The feature is trivial to implement, and I'm already using it in my own projects:
src/assertions.lua: local ok, err_actual = pcall(func, table.unpack(arguments,3,arguments.n))

screen shot 2015-05-12 at 9 12 51 am

Hope you agree with it, and thanks!

Add ability to suppress display of crumbs

In my research for this issue on Busted, the direct cause of the output is the decisions by this library to always output crumbs if they exist. For example in my specific case with the table formatter, this bit of code only considers whether the function exists (hard coded directly above) and whether crumbs exist. If they do, it outputs them.

There should be some way to run assert statements such as assert.same() with an extra argument or after setting some configuration value to suppress the output of crumbs and just get the pass/fail status in return.

Note that referenced code almost certainly isn't the place to fix this, somewhere upstream those formatters are getting called and they should be disabled at the source to suppress even the "Actual/Expected" strings.

stubbed function returning value when matching to call params

Is there a way to create a stub that would return a desired value when stubbed function is called?

local t = {do_something = function(msg) return msg end}
stub(t, "do_something").is_called_with("foo").then_return("bar")

print(t.do_something("foo")) -> prints "bar"
print(t.do_something("bar")) -> prints nothing or nil

or

stub(t, "do_something").is_called_with_any().then_return("bar")
print(t.do_something("foo")) -> prints "bar"
print(t.do_something("bar")) -> prints "bar"

Some thing lile assert.array(a).inlcude(element)

Is it possible to have array include test like follow?

assert.array(a).has(element)
assert.array(a).inlcude(element)
assert.array(a).has_element(element)

Currently I have to write a function include()

local function include(t, element)
  for _, v in ipairs(t) do
    if v == element then
      return true
    end
  end
  return false
end

-- Then:
-- ...
    assert.is_true(include(all, agent1))

Complete, exhaustive reference?

The examples in the readme and on the homepage are far from exhaustive. Where can I find a complete, exhaustive reference of all functions/assertions/matchers/whatever provided by luassert? Specific case: trying to use assert.are.near(), not knowing at which position the error/epsilon parameter goes, searching high and low, not finding the answer anywhere. Principally refusing to inspect source code because having to inspect source code in order to learn API is wrong.

assertions need refactoring of output

Currently an assertion takes 2 parameters

  1. state
  2. list of arguments, with an additional n field to indicate the number of elements (to take care of nil values)

It returns 1 value; a boolean being true if the assertion passed

On the side; the in coming arguments list is modified to prepare the arguments for proper display in the output. So actually it returns a second argument, through its second parameter (passed by reference).

imo it should actually get 3 parameters, the last being a (shallow) copy of the arguments table. And it should return 2, the boolean assertion result and an output table, being the modified copy of the arguments table.

feature request: highlighting diff in assert.are.same(...)

having output like this:

./spec/main_spec.lua:146: Expected objects to be the same.
Passed in:
(table): {
  [bid] = {
    [1] = {
      [price] = 105
      [quantity] = 3 }
    [2] = {
      [price] = 102
      [quantity] = 2 }
    [3] = {
      [price] = 100
      [quantity] = 1 } }
  [key] = 'A:B'
  [offer] = {
    [1] = {
      [price] = 110
      [quantity] = 1 }
    [2] = {
      [price] = 112
      [quantity] = 2 }
    [3] = {
      [price] = 115
      [quantity] = 3 } }
  [timestamp] = 37230.123456 }
Expected:
(table): {
  [bid] = {
    [1] = {
      [price] = 105
      [quantity] = 3 }
    [2] = {
      [price] = 102
      [quantity] = 2 }
    [3] = {
      [price] = 100
      [quantity] = 1 } }
  [key] = 'A:B'
  [name] = 'l2q'
  [offer] = {
    [1] = {
      [price] = 110
      [quantity] = 1 }
    [2] = {
      [price] = 112
      [quantity] = 2 }
    [3] = {
      [price] = 115
      [quantity] = 3 } }
  [timestamp] = 37230.123456 }

stack traceback:
        ./spec/main_spec.lua:146: in function <./spec/main_spec.lua:128>

its hard to understand where is the problem

It would be great to have some pointers on mismatches

for example, smth like this;


./spec/main_spec.lua:146: Expected objects to be the same.
Passed in:
> (table): {
    [bid] = {
      [1] = {
        [price] = 105
        [quantity] = 3 }
      [2] = {
        [price] = 102
        [quantity] = 2 }
      [3] = {
        [price] = 100
        [quantity] = 1 } }
    [key] = 'A:B'
    [offer] = {
      [1] = {
        [price] = 110
        [quantity] = 1 }
      [2] = {
        [price] = 112
        [quantity] = 2 }
      [3] = {
        [price] = 115
        [quantity] = 3 } }
    [timestamp] = 37230.123456 }
Expected:
> (table): {
    [bid] = {
      [1] = {
        [price] = 105
        [quantity] = 3 }
      [2] = {
        [price] = 102
        [quantity] = 2 }
      [3] = {
        [price] = 100
        [quantity] = 1 } }
    [key] = 'A:B'
>   [name] = 'l2q'
    [offer] = {
      [1] = {
        [price] = 110
        [quantity] = 1 }
      [2] = {
        [price] = 112
        [quantity] = 2 }
      [3] = {
        [price] = 115
        [quantity] = 3 } }
    [timestamp] = 37230.123456 }

In example above there is a marker which points to values that are not same:
tables are diferent, and the value of name is different (nil vs 'l2q')

so it becomes very easy to find out problem

util.deepcompare fails with __pairs

I have 2 objects that have a __pairs metamethod on them.
When I try and use assert.same on them, they return false, as they don't have an __index for the 'keys' returned by my __pairs.

One potential fix is to use next, t instead of pairs(t) so that __pairs is not used.
An alternative solution is to use pairs() to iterate through each object, building a temporary list of pairs, and then comparing those lists.

Don't hide nested tables when showing values marked with crumbs

When comparing two deep tables and there are minor differences, luassert helpfully marks keys on the path to different value with asterisks, but unhelpfully applies depth limit anyway, replacing subtables with { ... more }. It would be nice if depth limit wasn't applied to values marked with crumbs.

assert.spy.was.called_with() should have variants just check equality.

Lets see and example:

local Foo = {}
function Foo:add(Bar)
  Bar.callback(self)
  self.bar = Bar
end

local Bar = {}
function Bar.callback() end

describe('Foo', function()
  it(':add() calls Bar.callback()', function()
    local s = spy.on(Bar, 'callback')
    Foo:add(Bar) -- make a deep copy of Foo, before Foo.bar sets to Bar
    assert.spy(s).was.called(1) -- OK
    assert.spy(s).was.called_with(Foo) -- Failure, because Foo modified after callback...
  end)
end)
  1. The Foo:add() calls the spy and spy make a deep copy of Foo as FooCopy
  2. Then Foo:add() modifies Foo by add a Foo.bar = Bar
  3. The assert.spy(s).was.called_with(Foo) Failure because the Foo was not same as FooCopy.

So, I think it is needs that we have option in spy or called_with just keep a reference to its calling args and compare args by == (or something else).

Unable to assert stubs were called with references to self using `:` syntax.

I think the easiest way to explain this is with an example:

Let us assume I have the following object.

local MyObject = { name = 'default' }
-- A method that will be stubbed.
function MyObject:mock_method( arg1 ) end

Now we add a method to MyObject that will first make a call to the stub, then change the value of the 'name' field.

function MyObject:do_something_to_change_state()

  -- First call the mocked method which will store a deep 
  -- copy of "self" when self.name == 'default'
  self:mock_method( "hello" )
  self.mock_method( self, 'hello')

  -- Then change self.name to a new value.
  self.name = "Hey! I'm Different"
end

Later in a test we stub out mock_method and attempt to assert that it was called on the table referenced by MyObject.

stub( MyObject, 'mock_method')

MyObject:do_something_to_change_state()

-- This assertion fails because the first argument's name field still equals 'default'
-- and MyObject.name == "Hey! I'm Different" instead of 'default'
assert.stub( MyObject.mock_method ).was.called_with( MyObject )

The assertion will fail because the call history made a deep copy of MyObject when MyObject was in a different state.

I don't really have any ideas how to work around this. I feel like in every other case I would absolutely want the state to be copied for deep comparison.

I could always just say "I don't care" about the first argument but that solution feels unsatisfactory. Do you have any ideas on how I could go about dealing with this case?

assert doesn't return extra arguments

Plain assert does not currently pass it's arguments on.

I found out while trying to use a library that uses assert internally:
Eg, the code below exhibits the problem:

function foo()
    if something then
        return nil , "error message"
    else
        return "abc" , "xyz"
    end
end

local x1, x2 = assert ( foo() ) -- With luassert x2 is nil.

Fix requires rewriting the function at https://github.com/Olivine-Labs/luassert/blob/2faa83264eb0439bd2772d80bfd4ce8198986105/src/assert.lua#L155

Control characters make output hard to read

Example:

return  require"luassert".are.equal("a\rb", "\r")
Expected objects to be equal.
Passed in:
'string) '
Expected:
b'tring) 'a
stack traceback:
	[C]: in function 'error'
	...luarocks/2.4.2_5.1/share/lua/5.1/luassert/assert.lua:51: in function <...luarocks/2.4.2_5.1/share/lua/5.1/luassert/assert.lua:25>
	(tail call): ?
	[C]: ?

Expected results (or similar, just something else than writing on top of the output):

return  require"luassert".are.equal("a\rb", "\r")
Expected objects to be equal.
Passed in:
(string) '
'
Expected:
(string) 'a
b'
stack traceback:
	[C]: in function 'error'
	...luarocks/2.4.2_5.1/share/lua/5.1/luassert/assert.lua:51: in function <...luarocks/2.4.2_5.1/share/lua/5.1/luassert/assert.lua:25>
	(tail call): ?
	[C]: ?

This seems to come from Lua interpreter:

> = "a\rb"
b

Actually from terminal.

The extract_keys method may fail if the token has three or more words

https://github.com/Olivine-Labs/luassert/blob/36fc3af9621696a9dfcf71c0bcd25cdbc9475cf8/src/util.lua#L322-L329

At line 328, the longkey maybe wrong in further iterations.

For example: if the tokens is {'is', 'match', 'error'}, the longkey variable will be match_match_error at the second iteration in the inner while loop. So it may fail to get the longest matching key when the key has three or more words.

Here is the potential fix which I brought up:

-      longkey = (token .. '_' .. key)
+      longkey = i > 1 and (tokens[i-1] .. '_' .. key) or key

release?

@DorianGray any chance to get a release?

we'd really like to get #144 out so we can clean up our ugly hacks to work around it

is_true, is_false and is_type may throw an error on Lua >= 5.2.2

Lua 5.2.2 has a check in table.insert so it's imposible to insert outside the array part:

table.insert({}, 2, false) -- OK on Lua 5.1/Lua 5.2.1/LuaJIT, error on Lua 5.2.2

This may lead to errors here under Lua 5.2.2. In particular, it makes a few tests from busted spec fail.

As a side note, it would be nice to have at least automated testing on travis for Lua 5.2. I've made a script to help testing in several environments; here is example usage.

Edit: this only happens with Lua >= 5.2.2

feature request: string pattern matching

I'm testing code which uses the common

ok, retval = func( ... )

paradigm, where retval is an error message/object if ok is false. Just as with error messages tossed via exceptions (and handled by has_error) it would be nice to be able to compare the result with a match rather than an exact equality, e.g.

assert.are.matches( '.*: missing file', retval )

Thanks for a great testing framework!
Diab

Feature request: Print passed in and expected arguments for assert.spy

When using assert spy to check function call with defined arguments, a wrong argument is not displayed. Test example:

    it("#42 lua assert spy test", function()
        local someTableWithFunc = {foo = function(myArg)end}
        spy.on(someTableWithFunc, "foo")
        someTableWithFunc.foo(2)
        assert.spy(someTableWithFunc.foo).was_called_with(1)
    end)

The following error is displayed:

$ busted -t 42
0 successes / 1 failure / 0 errors / 0 pending : 0.077245 seconds
Failure → spec/takeawayDischarger_spec.lua @ 390
Takeaway discharger construction Unit test #42 lua assert spy test
spec/takeawayDischarger_spec.lua:394: Function was not called with the arguments

Feature request: support for "eventually" (willing to PR)

Hi there!

I was playing around with busted for a project I'm working on. I have a use-case for something like the following:

assert.eventually(f).returns(x)

That is, a way to provide a function that is repeatedly called at some cadence, until the provided function returns an expected result. Specifically in my case, I wanted to assert that after starting a subprocess I expect that process to eventually exist on my system. Then when I issue a kill I expect that process to die eventually.

Other similar test frameworks, such as ginkgo in the go ecosystem, have support for something like Eventually(f).Should(Equal(x)). This is useful for firing off async instructions and waiting for a thing to happen.

I would suggest, as with ginkgo, that the timeout and check interval of eventually be configurable and default to a timeout of 1 second and a interval of 50 milliseconds (where the platform supports it). This could be implemented as optional arguments to the eventually matcher or as additional words for a more natural read. For example:

assert.eventually(f, 10, 0.1).returns(x)
assert.eventually(f).within(10).every(0.01).returns(x)

I'm not sure how controversial a suggestion this is, so here's more controversy. Maybe we could provide a more human readable experience for the optional args:

assert.eventually(f).within("10s").every("100ms").returns(x)

Bonus (?)

If the idea above is well received, I'm happy to also provide a PR for something like:

assert.consistently(f).returns(x)

The usage here would be similar to the above.

I'm happy to PR all of the above - let me know what you think!

Thanks!

Can I pass state between modifiers/assertions?

I'd like to store a value, and then have other elements in the chain act on that value.

Example;

local r = http:get( ... some request ...)
assert.response(r).has.status(200)
local value = assert.response(r).has.header("Content-Length")
assert.are.equal(123, value)

So basically status and header assertions (which I can write myself and add to luassert) would operate on the value provided by the response modifier.

It's related to/prerequisite for #135 I think

I tried to add a value to the state parameter that is passed along the chain, but is was unreliable due to all the meta-table magic the state object has. So I ended up with an ugly wrapper on assert that would reset a custom state table (upvalue) on each call, and the modifiers storing data in that upvalue.

What would be the right approach to implement this?

assertions for log files

Quite common we need to validate behaviour by checking log files. Some helper assertions would be nice.

Issues:

  • applications don't always restart with clean logs in-between tests; so a start marker would be necessary
  • log file might not exist when start marker is called?
  • assertions for "contains"
  • needs filename where to look
local logfile = require "luassert.logfile"

it("checks a log file", function()
    local log = logfile("./logs/error.log")   -- sets a start marker (file size?), should not fail if file doesn't exist

    -- do stuff 

    assert.logfile(log).exists()
    assert.logfile(log).contains("some Lua pattern", [occurrences])
end)

is_calleable assertion

It would be useful to have an is_calleable assertion that tests whether is_function(argument) or is_function(debug.getmetatable(argument)._call).

issues comparing tables because of ordering

I am testing lua tables and decoded json output for REST APIs
and my tests keep failing because of table ordering. especially those without keys (AKA arrays)

I tried .same and .equals and I am getting same behavior
is there a better assertion function I could use ?

even, sometimes, the tables looks exactly the same...

Passed in:

(table) {

  [1] = 'dev'

  [2] = 'test' }

Expected:

(table) {

  [1] = 'dev'

  [2] = 'test' }

thanks

An error in the logic of util.deepcompare?

The current implementation of util.deepcompare has a result that is counter-intuitive (to me) in the following case:

    local foo = {4}
    local bar = {4}
    local mt = {}
    local function always_true()
        return true
    end
    mt.__eq = always_true
    setmetatable(foo, mt)

According to util.deepcompare, foo and bar are the same, but that seems wrong. (They don't both have the same metatable. bar has no metatable at all.) The problem comes in the fourth line here in util.deepcompare:

  local mt1 = debug.getmetatable(t1)
  local mt2 = debug.getmetatable(t2)
  -- would equality be determined by metatable __eq?
  if mt1 and mt1 == mt2 and mt1.__eq then
    -- then use that unless asked not to
    if not ignore_mt then return t1 == t2 end
  else -- we can skip the deep comparison below if t1 and t2 share identity
    if t1 == t2 then return true end
  end

Because the two tables do not have equal metatables, they are not compared using ==. Instead, they are handed off to the key, value checking further down. Since they both have the same single item—4—they appear the same by those sorts of checks.

This may be a judgment call, but util.deepcompare seems to me to give the wrong result. If two tables are being compared, and one has a metatable and the other doesn't, then that very fact means they are not (at a deep level) the same. underscore and cwtest would consider foo and bar different because they handle the metatable branch the following way:

    -- cwtest
    local mt = getmetatable(t1)
    if mt and mt.__eq then return t1 == t2 end

    -- underscore
    local mt = getmetatable(o1)
    if not ignore_mt and mt and mt.__eq then return o1 == o2 end

Sorry for the novel, but I wanted to see if the result was expected by you before making a PR to change anything. Thanks.

Edit: After thinking this over, I now think it's even more complicated. util.deepcompare is wrong to consider foo and bar the same table, but underscore and cwtest have a parallel problem. In their case, the problem is about order: what if the second (but not the first) table has a metatable with .__eq? Their way of handling things won't detect this, and they will wrongly call two subtly different tables the same.

So the full logic should be this:

    local mt1 = getmetatable(t1)
    local mt2 = getmetatable(t2)
    if (mt1 and mt1.__eq) or (mt2 and mt2.__eq) then
        return t1 == t2
    end

/ping @catwell and @mirven

Mock does not allow for easy de-mock

The mock function modifies a table in-place but provides no method of de-mocking it. Thus if you wish to re-use a table you must duplicate it and mock that instead.
This is in contrast to spies and stubs both of which have a revert method

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.