Skip to main content

The Jest Object

Jest

The methods in the jest object help create mocks and let you control Jest Roblox's overall behavior.

Deviation

It must be imported explicitly from JestGlobals.

local jest = require(Packages.Dev.JestGlobals).jest

Methods

Mock Modules

jest.mock(module, factory)

Jest API Change

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.

mockedModule.lua
return {}
__tests__/testMockedModule.spec.lua
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)

Jest API Change

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.

__tests__/testMockedModule.spec.lua
it("mockedModule should not be mocked", function()
jest.unmock(Workspace.mockedModule)
mockedModule = require(Workspace.mockedModule)
expect(mockedModule).toEqual({})
end)

jest.requireActual(module)

Jest API Change

Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not.

__tests__/testMockedModule.spec.lua
it("mockedModule also should not be mocked", function()
mockedModule = jest.requireActual(Workspace.mockedModule)
expect(mockedModule).toEqual({})
end)

jest.isolateModules(fn)

Jest Aligned

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()

Jest Aligned

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)

Jest Deviation

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)

Jest Aligned

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)
info

The jest.spyOn(object, methodName, accessType?) variant is not currently supported in jest-roblox.

jest.clearAllMocks()

Jest Aligned

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()

Jest Aligned

Resets the state of all mocks. Equivalent to calling .mockReset() on every mocked function.

Returns the jest object for chaining.

Mock Timers

jest.useFakeTimers()

Jest Deviation

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()

Jest Aligned

Instructs Jest Roblox to use the real versions of the standard timer functions.

Returns the jest object for chaining.

jest.runAllTimers()

Jest Deviation

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 delays 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)

Jest Aligned

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()

Jest Aligned

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)

Jest Aligned

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()

Jest Aligned

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()

Jest Aligned

Returns the number of fake timers still left to run.

jest.setSystemTime(now?: number | DateTime)

Jest Aligned

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()

Jest Aligned

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)

Roblox only

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()

Roblox only

jest.getEngineFrameTime gets the frame time by which timers are processed.

Mock Globals

jest.globalEnv

Roblox only

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().

danger

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 (use jest.mock() instead)
  • task scheduling functions (use Timer Mocks instead)