The Jest Object
The methods in the jest
object help create mocks and let you control Jest Roblox's overall behavior.
It must be imported explicitly from JestGlobals
.
local jest = require(Packages.Dev.JestGlobals).jest
Methods
- Mock Modules
- Mock Functions
- Mock Timers
jest.useFakeTimers()
jest.useRealTimers()
jest.runAllTimers()
jest.advanceTimersByTime(msToRun)
jest.runOnlyPendingTimers()
jest.advanceTimersToNextTimer(steps)
jest.clearAllTimers()
jest.getTimerCount()
jest.setSystemTime(now?: number | DateTime)
jest.getRealSystemTime()
jest.setEngineFrameTime(frameTimeMs)
jest.getEngineFrameTime()
- Mock Globals
Mock Modules
jest.mock(module, factory)
Mocks a module with an mocked version when it is being required. The second argument must be used to specify the value of the mocked module.
return {}
beforeEach(function()
jest.resetModules()
jest.mock(Workspace.mockedModule, function()
return {
default = jest.fn(function() return 42 end),
foo = jest.fn(function() return 43 end)
}
end)
end)
local mockedModule
it("mockedModule should be mocked", function()
mockedModule = require(Workspace.mockedModule)
expect(mockedModule.default()).toBe(42)
expect(mockedModule.foo()).toBe(43)
end)
Modules that are mocked with jest.mock
are mocked only for the file that calls jest.mock
. Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module.
Returns the jest
object for chaining.
jest.unmock(module)
Indicates that the module system should never return a mocked version of the specified module from require()
(e.g. that it should always return the real module).
Returns the jest
object for chaining.
it("mockedModule should not be mocked", function()
jest.unmock(Workspace.mockedModule)
mockedModule = require(Workspace.mockedModule)
expect(mockedModule).toEqual({})
end)
jest.requireActual(module)
Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not.
it("mockedModule also should not be mocked", function()
mockedModule = jest.requireActual(Workspace.mockedModule)
expect(mockedModule).toEqual({})
end)
jest.isolateModules(fn)
jest.isolateModules(fn)
creates a sandbox registry for the modules that are loaded inside the callback function. This is useful to isolate specific modules for every test so that local module state doesn't conflict between tests.
local myModule
jest.isolateModules(function()
myModule = require(Workspace.MyModule)
end)
local otherCopyOfMyModule = require(Workspace.MyModule)
jest.resetModules()
Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests.
Example:
local sum1 = require(Workspace.sum)
jest.resetModules()
local sum2 = require(Workspace.sum)
expect(sum1).never.toBe(sum2)
-- Both sum modules are separate "instances" of the sum module.
Example in a test:
beforeEach(function()
jest.resetModules()
end)
test('works', function()
local sum = require(Workspace.sum)
end)
test('works too', function()
local sum = require(Workspace.sum)
-- sum is a different copy of the sum module from the previous test.
end)
Returns the jest
object for chaining.
Mock Functions
jest.fn(implementation)
Returns a new, unused mock function. Optionally takes a mock implementation.
jest.fn()
returns a callable table as the first return value and a function as the second return value. See the deviation note.
local mockFn = jest.fn()
mockFn()
expect(mockFn).toHaveBeenCalled()
-- With a mock implementation:
local returnsTrue = jest.fn(function() return true end)
print(returnsTrue()) -- true
jest.spyOn(object, methodName)
Creates a mock function similar to jest.fn
but also tracks calls to object[methodName]
. Returns a Jest mock function.
Note: By default, jest.spyOn
also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName):mockImplementation(function() ... end)
or object[methodName] = jest.fn(function ... end)
Example:
local video = {
play = function()
return true
end
}
return video
Example test:
local video = require(Workspace.video);
test("plays video", function()
local spy = jest.spyOn(video, "play")
local isPlaying = video.play()
expect(spy).toHaveBeenCalled()
expect(isPlaying).toBe(true)
spy:mockRestore()
end)
The jest.spyOn(object, methodName, accessType?)
variant is not currently supported in jest-roblox.
jest.clearAllMocks()
Clears the mock.calls
, mock.instances
and mock.results
properties of all mocks. Equivalent to calling .mockClear()
on every mocked function.
This can be included in a beforeEach()
block in your text fixture to clear out the state of all mocks before each test.
Returns the jest
object for chaining.
jest.resetAllMocks()
Resets the state of all mocks. Equivalent to calling .mockReset()
on every mocked function.
Returns the jest
object for chaining.
Mock Timers
jest.useFakeTimers()
Instructs Jest Roblox to use fake versions of the standard Lua and Roblox timer functions. The following timers are mocked:
delay
tick
time
os
os.time
os.clock
task.delay
task.delay
task.cancel
task.wait
DateTime
Returns the jest
object for chaining.
jest.useRealTimers()
Instructs Jest Roblox to use the real versions of the standard timer functions.
Returns the jest
object for chaining.
jest.runAllTimers()
Exhausts the macro-task queue (i.e., all tasks queued by delay
).
When this API is called, all pending macro-tasks will be executed. If those tasks themselves schedule new tasks, those will be continually exhausted until there are no more tasks remaining in the queue.
This is often useful for synchronously executing delay
s during a test in order to synchronously assert about some behavior that would only happen after the delay
callbacks executed. See the Timer mocks doc for more information.
jest.advanceTimersByTime(msToRun)
Executes only the macro task queue (i.e., all tasks queued by delay
).
When this API is called, all timers are advanced by msToRun
milliseconds. All pending "macro-tasks" that have been queued, and would be executed within this time frame will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue, that should be run within msToRun
milliseconds.
jest.runOnlyPendingTimers()
Executes only the macro-tasks that are currently pending (i.e., all tasks queued by delay
). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call.
This is useful for scenarios such as one where the module being tested schedules a delay()
whose callback schedules another delay()
recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time.
jest.advanceTimersToNextTimer(steps)
Advances all timers by the needed seconds so that only the next timeouts/intervals will run.
Optionally, you can provide steps
, so it will run steps
amount of next timeouts/intervals.
jest.clearAllTimers()
Removes any pending timers from the timer system.
This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future.
jest.getTimerCount()
Returns the number of fake timers still left to run.
jest.setSystemTime(now?: number | DateTime)
Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to jest.setSystemTime()
.
jest.getRealSystemTime()
When mocking time, DateTime.now()
will also be mocked. If you for some reason need access to the real current time, you can invoke this function.
jest.setEngineFrameTime(frameTimeMs)
jest.setEngineFrameTime
sets the frame time, in milliseconds, by which all advance timer methods process timers. frameTimeMs
must be a value greater than or equal to 0; by default, frameTimeMs
is set to 0
(i.e. continuous time).
jest.getEngineFrameTime()
jest.getEngineFrameTime
gets the frame time by which timers are processed.
Mock Globals
jest.globalEnv
jest.globalEnv
represents the function environment table of the current test.
You can use it with jest.spyOn()
in place of an
object, to mock the implementation of a function such as print()
which lives
in the environment table.
You can also index into globalEnv
to represent global libraries, such as
jest.globalEnv.math
, which allows you to mock the implementation of library
functions such as math.random()
.
Finally, functions from the original function environment are available as
members of globalEnv
, for example, the original implementation of print()
(bypassing any current mocks) can be called via jest.globalEnv.print()
.
Jest Roblox does not support mocking all globals - only a few are whitelisted. If you try to mock a global which is not whitelisted, you will see an error message that looks similar to this:
Jest does not yet support mocking the require global.
Most notably, Jest Roblox does not support mocking these globals:
game:GetService()
and other Instance methods (an API for this is being investigated)- the
require()
function (usejest.mock()
instead) - task scheduling functions (use Timer Mocks instead)