Skip to content

Commit ebfb67a

Browse files
committed
Initial messy implementation
0 parents  commit ebfb67a

File tree

4 files changed

+344
-0
lines changed

4 files changed

+344
-0
lines changed

firstpass.lua

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
local expectation
2+
3+
local function mock(m)
4+
expectation.fn = m
5+
return expectation
6+
end
7+
8+
expectation = {
9+
expected_calls = {},
10+
11+
call_count = 0,
12+
13+
ordered = false,
14+
15+
should_be_called_with = function(...)
16+
expectation.expected_calls[#expectation.expected_calls + 1] = {
17+
fn = expectation.fn,
18+
args = table.pack(...),
19+
ordered = expectation.ordered
20+
}
21+
expectation.fn = nil
22+
expectation.ordered = false
23+
return expectation
24+
end,
25+
26+
should_be_called = function()
27+
return expectation.should_be_called_with()
28+
end,
29+
30+
and_then = function(expectation)
31+
-- do some jazz where the expectations are combined? with order!
32+
expectation.ordered = true
33+
return expectation
34+
end,
35+
36+
mock = mock,
37+
38+
check = function()
39+
assert(#expectation.expected_calls == expectation.call_count)
40+
end,
41+
42+
when = function(f)
43+
f()
44+
expectation.check()
45+
end,
46+
47+
after = function(...)
48+
return expectation.when(...)
49+
end,
50+
51+
args_same = function(t1, t2)
52+
if #t1 ~= #t2 then return false end
53+
54+
for k in ipairs(t1) do
55+
if t2[k] ~= t1[k] then return false end
56+
end
57+
58+
return true
59+
end,
60+
61+
mock_was_called = function(m, args)
62+
assert(#expectation.expected_calls > 0)
63+
64+
expectation.call_count = expectation.call_count + 1
65+
66+
assert(expectation.expected_calls[expectation.call_count].fn == m)
67+
assert(expectation.args_same(expectation.expected_calls[expectation.call_count].args, args))
68+
end
69+
}
70+
71+
local function m1(...)
72+
expectation.mock_was_called(m1, table.pack(...))
73+
end
74+
75+
local function m2(...)
76+
expectation.mock_was_called(m2, table.pack(...))
77+
end
78+
79+
local function m1_should_be_called()
80+
return mock(m1).should_be_called()
81+
end
82+
83+
local function m2_should_you_know()
84+
return mock(m2).should_be_called_with(1, 2, 3)
85+
end
86+
87+
-- m1_should_be_called().
88+
-- and_then(m2_should_you_know()).
89+
-- after(function() m1(); m2(1, 2, 3) end)
90+
91+
mock(m1).should_be_called().
92+
and_then(mock(m2).should_be_called_with(1, 2, 3)).
93+
when(function() m1(); m2(1, 2, 3) end)

moq.lua

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
local subscriber
2+
3+
local function mock_handle(callback, thunk)
4+
subscriber = callback
5+
thunk()
6+
subscriber = nil
7+
end
8+
9+
local function mock_called(m, args)
10+
return subscriber(m, args)
11+
end
12+
13+
14+
15+
ExpectedCall = {}
16+
17+
function ExpectedCall:new(f, args)
18+
local o = {
19+
_f = f,
20+
_args = args,
21+
_return = {}
22+
}
23+
24+
self.__index = self
25+
setmetatable(o, self)
26+
27+
return o
28+
end
29+
30+
function ExpectedCall:functionMatches(f)
31+
return _f == self.f
32+
end
33+
34+
function ExpectedCall:argsMatch(args)
35+
if #self._args ~= #args then return false end
36+
37+
for k in ipairs(self._args) do
38+
if self._args[k] ~= args[k] then return false end
39+
end
40+
41+
return true
42+
end
43+
44+
function ExpectedCall:setReturnValues(...)
45+
self._return = table.pack(...)
46+
end
47+
48+
function ExpectedCall:getReturnValues(...)
49+
return table.unpack(self._return)
50+
end
51+
52+
53+
54+
MockExpectation = {}
55+
56+
function MockExpectation:new(m)
57+
local o = {
58+
_m = m,
59+
_calls = {}
60+
}
61+
62+
self.__index = self
63+
setmetatable(o, self)
64+
65+
return o
66+
end
67+
68+
function MockExpectation:andWillReturn(...)
69+
self._calls[#self._calls]:setReturnValues(...)
70+
return self
71+
end
72+
73+
function MockExpectation:when(thunk)
74+
local function called(m, args)
75+
assert(#self._calls > 0, 'unexpected call')
76+
77+
assert(self._calls[1]:functionMatches(m), 'unexpected function called')
78+
assert(self._calls[1]:argsMatch(args), 'unexpected arguments provided')
79+
80+
return table.remove(self._calls, 1):getReturnValues()
81+
end
82+
83+
mock_handle(called, thunk)
84+
85+
assert(#self._calls == 0, 'not all calls occurred')
86+
end
87+
88+
function MockExpectation:after(thunk)
89+
self:when(thunk)
90+
end
91+
92+
function MockExpectation:andThen(other)
93+
-- Need to handle ordering
94+
for _, call in ipairs(other._calls) do
95+
table.insert(self._calls, call)
96+
end
97+
98+
return self
99+
end
100+
101+
function MockExpectation:andAlso(other)
102+
-- Need to handle ordering
103+
for _, call in ipairs(other._calls) do
104+
table.insert(self._calls, call)
105+
end
106+
107+
return self
108+
end
109+
110+
function MockExpectation:shouldBeCalledWith(...)
111+
table.insert(self._calls, ExpectedCall:new(self._m, table.pack(...)))
112+
return self
113+
end
114+
115+
function MockExpectation:shouldBeCalled()
116+
return self:shouldBeCalledWith()
117+
end
118+
119+
120+
121+
local function mock(m)
122+
return MockExpectation:new(m)
123+
end
124+
125+
local function createMockFunction()
126+
local f
127+
128+
function f(...)
129+
return mock_called(f, table.pack(...))
130+
end
131+
132+
return f
133+
end
134+
135+
local function createMockMethod()
136+
local m
137+
138+
function m(...)
139+
local args = table.pack(...)
140+
table.remove(args, 1)
141+
return mock_called(m, args)
142+
end
143+
144+
return m
145+
end
146+
147+
local function createMockTable(t)
148+
local mocked = {}
149+
150+
for k, v in pairs(t) do
151+
if type(v) == 'function' then
152+
mocked[k] = createMockFunction()
153+
end
154+
end
155+
156+
return mocked
157+
end
158+
159+
local function createMockObject(o)
160+
local mocked = {}
161+
162+
for k, v in pairs(o) do
163+
if type(v) == 'function' then
164+
mocked[k] = createMockMethod()
165+
end
166+
end
167+
168+
return mocked
169+
end
170+
171+
return {
172+
mock = mock,
173+
createMockFunction = createMockFunction,
174+
createMockTable = createMockTable,
175+
createMockMethod = createMockMethod,
176+
createMockObject = createMockObject
177+
}

notes

Whitespace-only changes.

spec/moq_spec.lua

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
describe('The mock library', function()
2+
moq = require 'moq'
3+
4+
it('should work', function()
5+
local m1 = moq.createMockFunction()
6+
local m2 = moq.createMockFunction()
7+
8+
moq.mock(m1):shouldBeCalled():
9+
andAlso(moq.mock(m2):shouldBeCalledWith(1, 2, 3)):
10+
andThen(moq.mock(m2):shouldBeCalledWith(1):andWillReturn(4)):
11+
when(function()
12+
m1()
13+
m2(1, 2, 3)
14+
assert.is.equal(m2(1), 4)
15+
end)
16+
end)
17+
18+
it('should allow functions to be used to improve readability', function()
19+
local m1 = moq.createMockFunction()
20+
local m2 = moq.createMockFunction()
21+
22+
function somethingShouldHappen()
23+
return moq.mock(m1):shouldBeCalled()
24+
end
25+
26+
function anotherThingShouldHappen()
27+
return moq.mock(m2):shouldBeCalledWith(1, 2, 3)
28+
end
29+
30+
function yetAnotherThingShouldHappen()
31+
return moq.mock(m2):shouldBeCalledWith(1):andWillReturn(4)
32+
end
33+
34+
function codeUnderTestRuns()
35+
m1()
36+
m2(1, 2, 3)
37+
assert.is.equal(m2(1), 4)
38+
end
39+
40+
somethingShouldHappen():
41+
andAlso(anotherThingShouldHappen()):
42+
andThen(yetAnotherThingShouldHappen()):
43+
when(codeUnderTestRuns)
44+
end)
45+
46+
it('should allow a table of functions to be mocked', function()
47+
local someTable = {
48+
foo = function() end,
49+
bar = function() end
50+
}
51+
52+
mockedTable = moq.createMockTable(someTable)
53+
54+
moq.mock(mockedTable.foo):shouldBeCalledWith(1):andWillReturn(2):
55+
when(function() mockedTable.foo(1) end)
56+
end)
57+
58+
it('should allow an object with methods to be mocked', function()
59+
local someObject = {}
60+
61+
function someObject:foo()
62+
end
63+
64+
function someObject:bar()
65+
end
66+
67+
mockedObject = moq.createMockObject(someObject)
68+
69+
moq.mock(mockedObject.foo):shouldBeCalledWith(1):andWillReturn(2):
70+
when(function() mockedObject:foo(1) end)
71+
end)
72+
73+
-- multiple return values
74+
end)

0 commit comments

Comments
 (0)