Skip to main content

PlayerMock

In-memory stand-in for a Roblox Player. A real Player cannot be Instance.new'd and no client joins a headless test place, so a mock is a tagged Folder typed as a Player.

Guards keep their real-Player assert and add an explicit OR clause:

assert(player:IsA("Player") or PlayerMock.isMock(player), "Bad player")

Native members a Folder cannot expose are read through mock-only accessors, so call sites branch explicitly and the real-Player path stays plain member access:

local player = PlayerMock.new({ UserId = 12345, AccountAge = 30 })
player.Parent = game:GetService("Players")

local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId

PlayerMock.write(player, "AccountAge", 31)

Events follow the same shape through PlayerMock.getSignal, with PlayerMock.fireSignal as the test-side trigger:

local chatted = if PlayerMock.isMock(player) then PlayerMock.getSignal(player, "Chatted") else player.Chatted
maid:GiveTask(chatted:Connect(onChatted))

PlayerMock.fireSignal(player, "Chatted", "hello")

Results of argument-keyed engine calls (group rank, gamepass/asset ownership, ...) go through PlayerMock.writeLookup / PlayerMock.readLookup, named by the canonical Service.Method and keyed by the arguments the call turns on:

PlayerMock.writeLookup(player, "GroupService.GetRolesInGroupAsync", {
	IsMember = true,
	Roles = { { Name = "Admin", Rank = 230 } },
}, 372)

Properties

TAG

This item is read only and cannot be modified. Read Only
PlayerMock.TAG: string

The CollectionService tag every mock carries, and the channel PlayerMockService / PlayerMockServiceClient discover mocks through. Tag resolution is DataModel-scoped, so a mock becomes discoverable when it is parented in and drops out when it is destroyed or kicked.

Functions

new

PlayerMock.new(
overrides{[string]any}?--

Per-property seed values, keyed by native property name.

) → Player

Constructs a mock player, unparented -- the caller parents and/or maids it. Once parented into the DataModel it is discoverable place-wide in either realm (see PlayerMock.TAG).

Every stand-in property (see PlayerMock.read) is seeded from overrides or its default.

local player = PlayerMock.new({ UserId = 12345, DisplayName = "Quenty" })
player.Parent = game:GetService("Players")

isMock

PlayerMock.isMock(valueany) → boolean

Returns whether the given value is a PlayerMock. A foreign instance merely carrying PlayerMock.TAG is rejected.

assert(player:IsA("Player") or PlayerMock.isMock(player), "Bad player")

findFirstAncestorMock

PlayerMock.findFirstAncestorMock(instanceInstance) → Player?

Returns the nearest ancestor of instance that is a PlayerMock, or nil. The mock counterpart of FindFirstAncestorWhichIsA("Player"), which a mock's backing Folder is invisible to:

local player = instance:FindFirstAncestorWhichIsA("Player") or PlayerMock.findFirstAncestorMock(instance)

Like the engine call, the walk starts at the parent -- instance itself is never returned.

getMockByUserId

PlayerMock.getMockByUserId(userIdnumber) → Player?

Returns the mock in the DataModel whose UserId stand-in matches, or nil. The mock counterpart of Players:GetPlayerByUserId, for code paths keyed by userId alone with no player value in hand to isMock-branch on:

local mockPlayer = PlayerMock.getMockByUserId(userId)
if mockPlayer ~= nil then
	result = PlayerMock.readLookup(mockPlayer, "MarketplaceService.UserOwnsGamePassAsync", gamePassId)
else
	result = MarketplaceService:UserOwnsGamePassAsync(userId, gamePassId)
end

Like the engine call, only mocks in the game resolve. Seed UserIds uniquely -- the first match wins.

getMocks

PlayerMock.getMocks() → {Player}

Returns the mocks in the DataModel. The mock counterpart of Players:GetPlayers():

for _, player in Players:GetPlayers() do
	handlePlayer(player)
end
for _, player in PlayerMock.getMocks() do
	handlePlayer(player)
end

getMockAddedSignal

PlayerMock.getMockAddedSignal() → RBXScriptSignal

Returns the signal that fires when a mock enters the DataModel. The mock counterpart of Players.PlayerAdded, which mocks are invisible to:

maid:GiveTask(Players.PlayerAdded:Connect(handlePlayer))
maid:GiveTask(PlayerMock.getMockAddedSignal():Connect(handlePlayer))

Fires when a mock is parented in -- which is when PlayerMock.getMocks starts returning it -- not when it is constructed.

getMockRemovingSignal

PlayerMock.getMockRemovingSignal() → RBXScriptSignal

Returns the signal that fires when a mock leaves the DataModel, whether it was destroyed or merely unparented (which is how PlayerMock.kick ends a mock). The mock counterpart of Players.PlayerRemoving, connected alongside it like PlayerMock.getMockAddedSignal.

getMockFromCharacter

PlayerMock.getMockFromCharacter(characterInstance) → Player?

Returns the mock in the DataModel whose Character stand-in is the given model, or nil. The mock counterpart of Players:GetPlayerFromCharacter:

local player = Players:GetPlayerFromCharacter(model) or PlayerMock.getMockFromCharacter(model)

Like the engine call, only the exact character model matches -- a descendant part resolves nil.

read

PlayerMock.read(
playerPlayer,--

must be a PlayerMock

propertyPathInstancePathTableLike--

"UserId" or "GuiService.SelectedObject"

) → any

Reads a stand-in native property off a mock. Errors on anything that is not a PlayerMock, including a real Player, so call sites branch explicitly:

local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId

A bare name reads a Player property. A Service.Property path reads the mock's own copy of a client-global service member, which a headless server has only one of:

local selected = PlayerMock.read(player, "GuiService.SelectedObject")

write

PlayerMock.write(
playerPlayer,--

must be a PlayerMock

propertyPathInstancePathTableLike,--

"UserId" or "GuiService.SelectedObject"

valueany
) → ()

Mocks a native property on a mock, firing PlayerMock.getPropertyChangedSignal so observers see the change. Takes the same paths PlayerMock.read does.

PlayerMock.write(player, "AccountAge", 31)
PlayerMock.write(player, "GuiService.SelectedObject", button)

Writing Character = nil carries the engine's despawn semantics -- see PlayerMock.removeCharacter.

getPropertyChangedSignal

PlayerMock.getPropertyChangedSignal(
playerPlayer,--

must be a PlayerMock

propertyPathInstancePathTableLike--

"UserId" or "GuiService.SelectedObject"

) → RBXScriptSignal

Returns the signal that fires when the given stand-in property changes on a mock. Mock-only, like PlayerMock.read -- the real-Player path stays player:GetPropertyChangedSignal(propertyName).

callMethod

PlayerMock.callMethod(
playerPlayer,--

must be a PlayerMock

methodPathInstancePathTableLike,--

"Player.Kick" or "MarketplaceService.UserOwnsGamePassAsync"

...any--

the engine call's own arguments; for a lookup, what its answer turns on

) → ...any

Calls a native method on a mock, running whichever stand-in it has: a callback bound over the whole method through PlayerMock.bindMethod, otherwise an answer injected for these arguments through PlayerMock.writeLookup, otherwise what the domain models.

if PlayerMock.isMock(player) then
	PlayerMock.callMethod(player, "Player.AddReplicationFocus", part)
else
	player:AddReplicationFocus(part)
end

The path is validated against the engine's reflection, so a typo errors instead of silently standing in for a method production could never have called.

bindMethod

PlayerMock.bindMethod(
playerPlayer,--

must be a PlayerMock

methodPathInstancePathTableLike,--

"Player.Kick" or "Players.GetFriendsAsync"

callback((
playerPlayer,
...any
) → ...any)?,--

nil removes the binding

...any--

the arguments to bind over, or none for the whole method

) → () → ()

Binds a callback to stand in for a native method on one mock, displacing whatever PlayerMock.callMethod would otherwise run. Use it where a value is not enough and the stand-in has to compute -- an answer that varies per call, or a method the mock models no default for.

PlayerMock.bindMethod(player, "Players.GetFriendsAsync", function(_player, _userId)
	return if attempts > 1 then friends else error("Rate limited")
end)

The callback receives the mock followed by the call's own arguments. Binding again replaces the previous callback, and binding nil removes it; PlayerMock.unbindMethod is that same removal under a name that says so.

Passing the call's arguments narrows the binding to that one argument tuple, the way PlayerMock.writeLookup injects a value for one; passing none binds the whole method.

Returns a function that removes this binding, so a maid can hold it:

maid:GiveTask(PlayerMock.bindMethod(player, "Players.GetFriendsAsync", stubFriends))

It removes only the binding it came from -- after a rebind it is a no-op -- so a maid unwinding late cannot tear down a stand-in that replaced its own.

unbindMethod

PlayerMock.unbindMethod(
playerPlayer,--

must be a PlayerMock

methodPathInstancePathTableLike,--

"Player.Kick" or "Players.GetFriendsAsync"

...any--

the arguments the binding was made over, or none for the whole method

) → ()

Removes a callback bound through PlayerMock.bindMethod, so the method falls back to its modelled stand-in. The arguments are the ones the binding was made over; unbinding a method that was never bound is a no-op.

isMethodBound

PlayerMock.isMethodBound(
playerPlayer,--

must be a PlayerMock

methodPathInstancePathTableLike,--

"Player.Kick" or "Players.GetFriendsAsync"

...any--

the arguments the binding was made over, or none for the whole method

) → boolean

Returns whether a callback is currently bound for the method on this mock, over the arguments given or over the whole method when none are.

readLookup

PlayerMock.readLookup(
playerPlayer,--

must be a PlayerMock

domainInstancePathTableLike,--

a known lookup domain, e.g. "GroupService.GetRolesInGroupAsync"

...any--

the engine call's own arguments, the ones the answer turns on

) → any

Reads back what a mock answers for an argument-keyed engine call, the test-side name for PlayerMock.callMethod. The value is the raw engine result shape, so production parsing runs over it unchanged:

if PlayerMock.isMock(player) then
	return PlayerMock.readLookup(player, "GroupService.GetRolesInGroupAsync", groupId)
end
return GroupService:GetRolesInGroupAsync(player.UserId, groupId)

Effect-recording domains (e.g. StarterGui.SetCoreGuiEnabled) run the same machinery in the other direction: production writes through PlayerMock.writeLookup and the test reads here.

writeLookup

PlayerMock.writeLookup(
playerPlayer,--

must be a PlayerMock

domainInstancePathTableLike,--

a known lookup domain, e.g. "MarketplaceService.UserOwnsGamePassAsync"

valueany,--

must match the domain's result shape; nil removes the injection

...any--

the engine call's own arguments, the ones the answer turns on

) → ()

Injects the result a mock answers for an argument-keyed engine call -- PlayerMock.bindMethod over those arguments, with a constant in place of a callback. Passing nil removes the injection, leaving the domain to answer what it models again.

PlayerMock.writeLookup(player, "GroupService.GetRolesInGroupAsync", {
	IsMember = true,
	Roles = { { Name = "Admin", Rank = 230 } },
}, 372)
PlayerMock.writeLookup(player, "MarketplaceService.UserOwnsGamePassAsync", true, 12345)

loadCharacterAsync

PlayerMock.loadCharacterAsync(
playerPlayer,--

must be a PlayerMock

characterModel?--

the new character; nil builds a default R15 rig

) → Model

Emulates Player:LoadCharacterAsync() on a mock. The caller supplies the character model -- e.g. Players:CreateHumanoidModelFromUserId/FromDescription (both work in cloud test runs) or a hand-built rig -- or omits it to get a default R15 built from an empty HumanoidDescription (which may yield).

local character = PlayerMock.loadCharacterAsync(player, rig)

The sequence encodes the engine's avatar loading event ordering, which PlayerMock.spec asserts step by step:

  1. CharacterRemoving(old) fires while Character still points at the old, parented model
  2. Character nils, then the old character is destroyed
  3. the new rig is fully built before any signal fires
  4. Character is set to the new model
  5. the new character is parented to the Workspace
  6. CharacterAdded(new) fires
  7. HasAppearanceLoaded flips true and CharacterAppearanceLoaded(new) fires
  8. the call returns

CharacterAdded fires only during avatar loading, which is why a plain PlayerMock.write(player, "Character", model) deliberately does not fire it.

Each call also replaces the PlayerMock.getBackpack stand-in with a fresh empty one, like the engine does on respawn (minus the StarterPack copy). The first call additionally inserts the PlayerMock.getStarterGear stand-in, which later spawns keep.

loadMinimalCharacterAsync

PlayerMock.loadMinimalCharacterAsync(
playerPlayer--

must be a PlayerMock

) → Model

PlayerMock.loadCharacterAsync with a minimal hand-built rig -- an anchored HumanoidRootPart (the PrimaryPart) and a Humanoid. Building it never yields, so specs that only need a character spawn instantly:

local character = PlayerMock.loadMinimalCharacterAsync(playerMock)

removeCharacter

PlayerMock.removeCharacter(
playerPlayer--

must be a PlayerMock

) → ()

Emulates the character being removed with no replacement, i.e. player.Character = nil: CharacterRemoving fires while Character still points at the model, Character is set to nil, and the model is destroyed. No-op when no character is loaded.

Runs automatically when the mock is destroyed or kicked.

getBackpack

PlayerMock.getBackpack(
playerPlayer--

must be a PlayerMock

) → Backpack?

Returns the mock's current Backpack stand-in, or nil before the first spawn. It is a genuine Backpack parented to the mock, so production code observing its children works unchanged. PlayerMock.loadCharacterAsync replaces it with a fresh empty one on every spawn:

local character = PlayerMock.loadCharacterAsync(player, rig)
local backpack = assert(PlayerMock.getBackpack(player))
tool.Parent = backpack

getStarterGear

PlayerMock.getStarterGear(
playerPlayer--

must be a PlayerMock

) → StarterGear?

Returns the mock's current StarterGear stand-in, or nil before the first spawn. It is a genuine StarterGear parented to the mock, and unlike the Backpack it survives respawns. Consumers that dot-index player.StarterGear branch:

local starterGear = if PlayerMock.isMock(player)
	then PlayerMock.getStarterGear(player)
	else player.StarterGear

getPlayerGui

PlayerMock.getPlayerGui(
playerPlayer--

must be a PlayerMock

) → PlayerGui

Returns the mock's PlayerGui stand-in, parented at construction. It is really a Folder named "PlayerGui", so consumers branch instead of using FindFirstChildOfClass:

local playerGui = if PlayerMock.isMock(player)
	then PlayerMock.getPlayerGui(player)
	else player:FindFirstChildOfClass("PlayerGui")

PlayerGuiUtils branches this way internally, so its consumers work against a mock unchanged.

getPlayerScripts

PlayerMock.getPlayerScripts(
playerPlayer--

must be a PlayerMock

) → PlayerScripts

Returns the mock's PlayerScripts stand-in, parented at construction. Like the PlayerGui stand-in it is really a Folder, which can never satisfy an IsA("PlayerScripts") filter, so consumers observing the child by class branch on the class name:

local playerScriptsClassName = if PlayerMock.isMock(localPlayer) then "Folder" else "PlayerScripts"
RxInstanceUtils.observeLastNamedChildBrio(localPlayer, playerScriptsClassName, "PlayerScripts")

kick

PlayerMock.kick(
playerPlayer,--

must be a PlayerMock

messagestring?--

recorded for PlayerMock.getKickMessage; nil records ""

) → ()

Emulates Player:Kick(message) on a mock, performing the removal sequence rather than merely recording the call:

  1. the message is recorded for PlayerMock.getKickMessage
  2. the character is removed (see PlayerMock.removeCharacter)
  3. the mock leaves the DataModel (Parent = nil, not a destroy -- a held reference stays readable), so AncestryChanged genuinely fires
if PlayerMock.isMock(player) then
	PlayerMock.kick(player, reason)
else
	player:Kick(reason)
end

Players.PlayerRemoving is a Players-service event only the engine fires, so consumers of it cannot observe a mock kick -- observe AncestryChanged instead.

getKickMessage

PlayerMock.getKickMessage(
playerPlayer--

must be a PlayerMock

) → string?

Returns the message a mock was kicked with via PlayerMock.kick, or nil when it was never kicked. A kick with no message reads back as "". Stays readable after the kick, as long as the caller holds a reference.

addReplicationFocus

PlayerMock.addReplicationFocus(
playerPlayer,--

must be a PlayerMock

partBasePart
) → ()

Emulates Player:AddReplicationFocus(part) on a mock. The backing is a set, so adding a part already focused does nothing.

if PlayerMock.isMock(player) then
	PlayerMock.addReplicationFocus(player, part)
else
	player:AddReplicationFocus(part)
end

removeReplicationFocus

PlayerMock.removeReplicationFocus(
playerPlayer,--

must be a PlayerMock

partBasePart
) → ()

Emulates Player:RemoveReplicationFocus(part) on a mock. The removal half of the branch in PlayerMock.addReplicationFocus; removing a part that is not focused is a no-op.

getReplicationFocuses

PlayerMock.getReplicationFocuses(
playerPlayer--

must be a PlayerMock

) → {BasePart}

Returns the parts currently focused on a mock, in the order they were added. The engine has no counterpart -- a real Player's focuses can only be added and removed -- so this is the test-side reader for PlayerMock.addReplicationFocus / PlayerMock.removeReplicationFocus.

getSignal

PlayerMock.getSignal(
playerPlayer,--

must be a PlayerMock

eventPathInstancePathTableLike--

"Chatted" or "UserInputService.WindowFocused"

) → RBXScriptSignal

Reads a stand-in native event off a mock: the genuine native signal for events the backing Folder inherits from Instance, otherwise a signal a test fires through PlayerMock.fireSignal. The path is validated against the engine's reflection, so a typo errors instead of returning a signal that can never fire.

local chatted = if PlayerMock.isMock(player) then PlayerMock.getSignal(player, "Chatted") else player.Chatted

A bare name reads a Player event. A Service.Event path reads the mock's own copy of a client-global service event, which a headless server has only one of -- see PlayerMock.getServiceSignal.

fireSignal

PlayerMock.fireSignal(
playerPlayer,--

must be a PlayerMock

eventPathInstancePathTableLike,--

"Chatted" or "UserInputService.WindowFocused"

...any--

Event arguments delivered to connected handlers.

) → ()

Fires the backing signal for an event on a mock, so code connected through PlayerMock.getSignal observes the event as if the engine had fired it. Takes the same paths PlayerMock.getSignal does.

PlayerMock.fireSignal(player, "Chatted", "hello")
PlayerMock.fireSignal(player, "UserInputService.WindowFocused")

Events the backing Folder inherits from Instance resolve to genuine native signals, which only the engine fires, so they cannot be fired here.

getServiceSignal

PlayerMock.getServiceSignal(
playerPlayer,--

must be a PlayerMock

domainInstancePathTableLike--

a canonical Service.Event, e.g. "UserInputService.WindowFocused"

) → RBXScriptSignal

Reads a stand-in for a client service's event off a mock -- UserInputService.WindowFocused, UserInputService.InputEnded, and the like -- which a test fires through [PlayerMock.fireServiceSignal]:

local localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()
if localPlayer ~= nil and PlayerMock.isMock(localPlayer) then
	return PlayerMock.getServiceSignal(localPlayer, "UserInputService.WindowFocused")
end
return UserInputService.WindowFocused

Named sugar over PlayerMock.getSignal, which takes the same Service.Event path -- the mock is only where the backing lives.

Arguments cross a BindableEvent, so they are marshalled: EnumItems, numbers and strings arrive intact, but a table's methods and metatable do not survive. Hand a PlayerMock.makeInputObject stand-in to PlayerMock.fireInput instead when the handler calls methods on it.

fireServiceSignal

PlayerMock.fireServiceSignal(
playerPlayer,--

must be a PlayerMock

domainInstancePathTableLike,--

a canonical Service.Event, e.g. "UserInputService.WindowFocused"

...any--

Event arguments delivered to connected handlers.

) → ()

Fires the backing signal for a client service's event on a mock, so code connected through PlayerMock.getServiceSignal observes the event as if the engine had fired it. Named sugar over PlayerMock.fireSignal.

PlayerMock.fireServiceSignal(player, "UserInputService.WindowFocused")

bindInput

PlayerMock.bindInput(
playerPlayer,--

must be a PlayerMock

domainInstancePathTableLike,--

a known input domain, e.g. "ContextActionService.BindAction"

actionNamestring,
...any--

the engine call's remaining args, e.g. functionToBind, createTouchButton, ...inputTypes

) → ()

Emulates a context-restricted ContextActionService call on a mock. The args after the domain are the engine call's own, so a production mock branch is the identical call aimed at the mock:

local localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()
if localPlayer ~= nil and PlayerMock.isMock(localPlayer) then
	PlayerMock.bindInput(localPlayer, "ContextActionService.BindAction", "Drag", onDragAction, false, Enum.UserInputType.MouseButton2)
else
	ContextActionService:BindAction("Drag", onDragAction, false, Enum.UserInputType.MouseButton2)
end

Unbinding goes through the same entry point -- the domain names the operation:

PlayerMock.bindInput(localPlayer, "ContextActionService.UnbindAction", "Drag")

A test dispatches a bound action through PlayerMock.fireInput. Deliberately not modelled: touch buttons, priority routing, input-type routing, and the engine's bind stack. All bind domains share one action registry per mock, like the engine's, so rebinding a name replaces the callback.

isInputBound

PlayerMock.isInputBound(
playerPlayer,--

must be a PlayerMock

actionNamestring
) → boolean

Returns whether the given action is currently bound on a mock via PlayerMock.bindInput.

fireInput

PlayerMock.fireInput(
playerPlayer,--

must be a PlayerMock

actionNamestring,
userInputStateEnum.UserInputState,
inputObjectany?--

a real InputObject, a plain stand-in table, or a makeInputObject stand-in

) → Enum.ContextActionResult?

Dispatches a bound action on a mock, invoking the bound callback with (actionName, userInputState, inputObject) -- the engine's argument order -- and returning its result. Errors when the action is not bound.

PlayerMock.fireInput(player, "Drag", Enum.UserInputState.Begin, input)

inputObject is passed by reference, so hand it a real InputObject, a plain table of the fields the callback reads, or a PlayerMock.makeInputObject stand-in when the callback also needs :GetPropertyChangedSignal(...).

makeInputObject

PlayerMock.makeInputObject(propsInputObjectProps?) → table--

an InputObject stand-in

Builds a stand-in InputObject for PlayerMock.fireInput to hand a bound action, for handlers that read more than the raw fields -- in particular :GetPropertyChangedSignal("UserInputState"). A real InputObject is not Instance.new-able, so this is a plain table exposing the fields and that one method; drive the press lifecycle with :SetUserInputState(...):

local input = PlayerMock.makeInputObject({ UserInputType = Enum.UserInputType.Gamepad1, KeyCode = Enum.KeyCode.ButtonA })
PlayerMock.fireInput(mock, actionName, Enum.UserInputState.Begin, input)
input:SetUserInputState(Enum.UserInputState.End)

setSelectedGuiObject

PlayerMock.setSelectedGuiObject(
playerPlayer,--

must be a PlayerMock

guiObjectGuiObject?--

the focused object, or nil to clear

) → ()

Sets the mock's stand-in for GuiService.SelectedObject, or clears it with nil. A headless server has no PlayerGui, so the engine rejects GuiService.SelectedObject = obj outright and selection code branches:

local localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()
if localPlayer ~= nil and PlayerMock.isMock(localPlayer) then
	PlayerMock.setSelectedGuiObject(localPlayer, button)
else
	GuiService.SelectedObject = button
end

Named sugar over PlayerMock.write(player, "GuiService.SelectedObject", guiObject), which reads and writes the same per-mock store.

getSelectedGuiObject

PlayerMock.getSelectedGuiObject(
playerPlayer--

must be a PlayerMock

) → GuiObject?

Reads the mock's stand-in for GuiService.SelectedObject, or nil when nothing is selected. The read side of the same branch as PlayerMock.setSelectedGuiObject.

getSelectedGuiObjectChangedSignal

PlayerMock.getSelectedGuiObjectChangedSignal(
playerPlayer--

must be a PlayerMock

) → RBXScriptSignal

Returns the signal that fires when the mock's stand-in for GuiService.SelectedObject changes, standing in for GuiService:GetPropertyChangedSignal("SelectedObject").

setMockedLocalPlayer

PlayerMock.setMockedLocalPlayer(
playerPlayer?--

must be a PlayerMock in the DataModel, or nil to clear

) → () → ()--

Restores the previous designation. Safe to call more than once.

Designates a mock as the local player for the client realm, or clears it with nil. Read back through PlayerMock.getMockedLocalPlayer.

maid:GiveTask(PlayerMock.setMockedLocalPlayer(player))

Call this directly before booting bags to pre-designate -- matching production, where Players.LocalPlayer exists before any service runs -- and a booting PlayerMockServiceClient adopts the designation and owns its cleanup. After boot, designate through PlayerMockServiceClient.SetLocalPlayer instead.

The mock must already be parented into the DataModel, the designation being a tag that GetTagged only resolves for parented instances.

The returned disposer restores whatever was designated before this call, so nested designations unwind correctly. It is a no-op when the designation has since moved on, and calling it more than once is safe.

getMockedLocalPlayer

PlayerMock.getMockedLocalPlayer() → Player?

Returns the mock designated as the local player, or nil. This is only ever the mock -- there is deliberately no helper resolving the real Players.LocalPlayer, so call sites fall back explicitly and the real read stays visible to luau-lsp:

local localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Constructs a mock player, unparented -- the caller parents and/or maids it. Once parented into\nthe DataModel it is discoverable place-wide in either realm (see [PlayerMock.TAG]).\n\nEvery stand-in property (see [PlayerMock.read]) is seeded from `overrides` or its default.\n\n```lua\nlocal player = PlayerMock.new({ UserId = 12345, DisplayName = \"Quenty\" })\nplayer.Parent = game:GetService(\"Players\")\n```",
            "params": [
                {
                    "name": "overrides",
                    "desc": "Per-property seed values, keyed by native property name.",
                    "lua_type": "{ [string]: any }?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 92,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "isMock",
            "desc": "Returns whether the given value is a [PlayerMock]. A foreign instance merely carrying\n[PlayerMock.TAG] is rejected.\n\n```lua\nassert(player:IsA(\"Player\") or PlayerMock.isMock(player), \"Bad player\")\n```",
            "params": [
                {
                    "name": "value",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 126,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "findFirstAncestorMock",
            "desc": "Returns the nearest ancestor of `instance` that is a [PlayerMock], or nil. The mock counterpart\nof `FindFirstAncestorWhichIsA(\"Player\")`, which a mock's backing Folder is invisible to:\n\n```lua\nlocal player = instance:FindFirstAncestorWhichIsA(\"Player\") or PlayerMock.findFirstAncestorMock(instance)\n```\n\nLike the engine call, the walk starts at the parent -- `instance` itself is never returned.",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Player?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 143,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMockByUserId",
            "desc": "Returns the mock in the DataModel whose `UserId` stand-in matches, or nil. The mock counterpart\nof `Players:GetPlayerByUserId`, for code paths keyed by userId alone with no player value in hand\nto `isMock`-branch on:\n\n```lua\nlocal mockPlayer = PlayerMock.getMockByUserId(userId)\nif mockPlayer ~= nil then\n\tresult = PlayerMock.readLookup(mockPlayer, \"MarketplaceService.UserOwnsGamePassAsync\", gamePassId)\nelse\n\tresult = MarketplaceService:UserOwnsGamePassAsync(userId, gamePassId)\nend\n```\n\nLike the engine call, only mocks in the game resolve. Seed UserIds uniquely -- the first match wins.",
            "params": [
                {
                    "name": "userId",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Player?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 166,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMocks",
            "desc": "Returns the mocks in the DataModel. The mock counterpart of `Players:GetPlayers()`:\n\n```lua\nfor _, player in Players:GetPlayers() do\n\thandlePlayer(player)\nend\nfor _, player in PlayerMock.getMocks() do\n\thandlePlayer(player)\nend\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ Player }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 184,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMockAddedSignal",
            "desc": "Returns the signal that fires when a mock enters the DataModel. The mock counterpart of\n`Players.PlayerAdded`, which mocks are invisible to:\n\n```lua\nmaid:GiveTask(Players.PlayerAdded:Connect(handlePlayer))\nmaid:GiveTask(PlayerMock.getMockAddedSignal():Connect(handlePlayer))\n```\n\nFires when a mock is parented in -- which is when [PlayerMock.getMocks] starts returning it --\nnot when it is constructed.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 202,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMockRemovingSignal",
            "desc": "Returns the signal that fires when a mock leaves the DataModel, whether it was destroyed or\nmerely unparented (which is how [PlayerMock.kick] ends a mock). The mock counterpart of\n`Players.PlayerRemoving`, connected alongside it like [PlayerMock.getMockAddedSignal].",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 213,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMockFromCharacter",
            "desc": "Returns the mock in the DataModel whose `Character` stand-in is the given model, or nil. The\nmock counterpart of `Players:GetPlayerFromCharacter`:\n\n```lua\nlocal player = Players:GetPlayerFromCharacter(model) or PlayerMock.getMockFromCharacter(model)\n```\n\nLike the engine call, only the exact character model matches -- a descendant part resolves nil.",
            "params": [
                {
                    "name": "character",
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Player?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 230,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "read",
            "desc": "Reads a stand-in native property off a mock. Errors on anything that is not a [PlayerMock],\nincluding a real `Player`, so call sites branch explicitly:\n\n```lua\nlocal userId = if PlayerMock.isMock(player) then PlayerMock.read(player, \"UserId\") else player.UserId\n```\n\nA bare name reads a `Player` property. A `Service.Property` path reads the mock's own copy of a\nclient-global service member, which a headless server has only one of:\n\n```lua\nlocal selected = PlayerMock.read(player, \"GuiService.SelectedObject\")\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "propertyPath",
                    "desc": "`\"UserId\"` or `\"GuiService.SelectedObject\"`",
                    "lua_type": "InstancePathTableLike"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 253,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "write",
            "desc": "Mocks a native property on a mock, firing [PlayerMock.getPropertyChangedSignal] so observers see\nthe change. Takes the same paths [PlayerMock.read] does.\n\n```lua\nPlayerMock.write(player, \"AccountAge\", 31)\nPlayerMock.write(player, \"GuiService.SelectedObject\", button)\n```\n\nWriting `Character = nil` carries the engine's despawn semantics -- see [PlayerMock.removeCharacter].",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "propertyPath",
                    "desc": "`\"UserId\"` or `\"GuiService.SelectedObject\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "value",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 272,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getPropertyChangedSignal",
            "desc": "Returns the signal that fires when the given stand-in property changes on a mock. Mock-only,\nlike [PlayerMock.read] -- the real-Player path stays `player:GetPropertyChangedSignal(propertyName)`.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "propertyPath",
                    "desc": "`\"UserId\"` or `\"GuiService.SelectedObject\"`",
                    "lua_type": "InstancePathTableLike"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 284,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "callMethod",
            "desc": "Calls a native method on a mock, running whichever stand-in it has: a callback bound over the\nwhole method through [PlayerMock.bindMethod], otherwise an answer injected for these arguments\nthrough [PlayerMock.writeLookup], otherwise what the domain models.\n\n```lua\nif PlayerMock.isMock(player) then\n\tPlayerMock.callMethod(player, \"Player.AddReplicationFocus\", part)\nelse\n\tplayer:AddReplicationFocus(part)\nend\n```\n\nThe path is validated against the engine's reflection, so a typo errors instead of silently\nstanding in for a method production could never have called.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "methodPath",
                    "desc": "`\"Player.Kick\"` or `\"MarketplaceService.UserOwnsGamePassAsync\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "the engine call's own arguments; for a lookup, what its answer turns on",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "...any"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 312,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "bindMethod",
            "desc": "Binds a callback to stand in for a native method on one mock, displacing whatever\n[PlayerMock.callMethod] would otherwise run. Use it where a value is not enough and the stand-in\nhas to compute -- an answer that varies per call, or a method the mock models no default for.\n\n```lua\nPlayerMock.bindMethod(player, \"Players.GetFriendsAsync\", function(_player, _userId)\n\treturn if attempts > 1 then friends else error(\"Rate limited\")\nend)\n```\n\nThe callback receives the mock followed by the call's own arguments. Binding again replaces the\nprevious callback, and binding nil removes it; [PlayerMock.unbindMethod] is that same removal\nunder a name that says so.\n\nPassing the call's arguments narrows the binding to that one argument tuple, the way\n[PlayerMock.writeLookup] injects a value for one; passing none binds the whole method.\n\nReturns a function that removes this binding, so a maid can hold it:\n\n```lua\nmaid:GiveTask(PlayerMock.bindMethod(player, \"Players.GetFriendsAsync\", stubFriends))\n```\n\nIt removes only the binding it came from -- after a rebind it is a no-op -- so a maid unwinding\nlate cannot tear down a stand-in that replaced its own.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "methodPath",
                    "desc": "`\"Player.Kick\"` or `\"Players.GetFriendsAsync\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "callback",
                    "desc": "nil removes the binding",
                    "lua_type": "((player: Player, ...any) -> ...any)?"
                },
                {
                    "name": "...",
                    "desc": "the arguments to bind over, or none for the whole method",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "() -> ()"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 349,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "unbindMethod",
            "desc": "Removes a callback bound through [PlayerMock.bindMethod], so the method falls back to its\nmodelled stand-in. The arguments are the ones the binding was made over; unbinding a method that\nwas never bound is a no-op.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "methodPath",
                    "desc": "`\"Player.Kick\"` or `\"Players.GetFriendsAsync\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "the arguments the binding was made over, or none for the whole method",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 367,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "isMethodBound",
            "desc": "Returns whether a callback is currently bound for the method on this mock, over the arguments\ngiven or over the whole method when none are.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "methodPath",
                    "desc": "`\"Player.Kick\"` or `\"Players.GetFriendsAsync\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "the arguments the binding was made over, or none for the whole method",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 380,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "readLookup",
            "desc": "Reads back what a mock answers for an argument-keyed engine call, the test-side name for\n[PlayerMock.callMethod]. The value is the raw engine result shape, so production parsing runs\nover it unchanged:\n\n```lua\nif PlayerMock.isMock(player) then\n\treturn PlayerMock.readLookup(player, \"GroupService.GetRolesInGroupAsync\", groupId)\nend\nreturn GroupService:GetRolesInGroupAsync(player.UserId, groupId)\n```\n\nEffect-recording domains (e.g. `StarterGui.SetCoreGuiEnabled`) run the same machinery in the\nother direction: production writes through [PlayerMock.writeLookup] and the test reads here.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "domain",
                    "desc": "a known lookup domain, e.g. \"GroupService.GetRolesInGroupAsync\"",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "the engine call's own arguments, the ones the answer turns on",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 408,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "writeLookup",
            "desc": "Injects the result a mock answers for an argument-keyed engine call -- [PlayerMock.bindMethod]\nover those arguments, with a constant in place of a callback. Passing nil removes the injection,\nleaving the domain to answer what it models again.\n\n```lua\nPlayerMock.writeLookup(player, \"GroupService.GetRolesInGroupAsync\", {\n\tIsMember = true,\n\tRoles = { { Name = \"Admin\", Rank = 230 } },\n}, 372)\nPlayerMock.writeLookup(player, \"MarketplaceService.UserOwnsGamePassAsync\", true, 12345)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "domain",
                    "desc": "a known lookup domain, e.g. \"MarketplaceService.UserOwnsGamePassAsync\"",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "value",
                    "desc": "must match the domain's result shape; nil removes the injection",
                    "lua_type": "any"
                },
                {
                    "name": "...",
                    "desc": "the engine call's own arguments, the ones the answer turns on",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 430,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "loadCharacterAsync",
            "desc": "Emulates `Player:LoadCharacterAsync()` on a mock. The caller supplies the character model -- e.g.\n`Players:CreateHumanoidModelFromUserId`/`FromDescription` (both work in cloud test runs) or a\nhand-built rig -- or omits it to get a default R15 built from an empty `HumanoidDescription`\n(which may yield).\n\n```lua\nlocal character = PlayerMock.loadCharacterAsync(player, rig)\n```\n\nThe sequence encodes the engine's [avatar loading event ordering](https://devforum.roblox.com/t/avatar-loading-event-ordering-improvements/269607),\nwhich PlayerMock.spec asserts step by step:\n\n1. `CharacterRemoving(old)` fires while `Character` still points at the old, parented model\n2. `Character` nils, then the old character is destroyed\n3. the new rig is fully built before any signal fires\n4. `Character` is set to the new model\n5. the new character is parented to the Workspace\n6. `CharacterAdded(new)` fires\n7. `HasAppearanceLoaded` flips true and `CharacterAppearanceLoaded(new)` fires\n8. the call returns\n\n`CharacterAdded` fires only during avatar loading, which is why a plain\n`PlayerMock.write(player, \"Character\", model)` deliberately does not fire it.\n\nEach call also replaces the [PlayerMock.getBackpack] stand-in with a fresh empty one, like the\nengine does on respawn (minus the StarterPack copy). The first call additionally inserts the\n[PlayerMock.getStarterGear] stand-in, which later spawns keep.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "character",
                    "desc": "the new character; nil builds a default R15 rig",
                    "lua_type": "Model?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Model"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 472,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "loadMinimalCharacterAsync",
            "desc": "[PlayerMock.loadCharacterAsync] with a minimal hand-built rig -- an anchored `HumanoidRootPart`\n(the `PrimaryPart`) and a `Humanoid`. Building it never yields, so specs that only need *a*\ncharacter spawn instantly:\n\n```lua\nlocal character = PlayerMock.loadMinimalCharacterAsync(playerMock)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Model"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 488,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "removeCharacter",
            "desc": "Emulates the character being removed with no replacement, i.e. `player.Character = nil`:\n`CharacterRemoving` fires while `Character` still points at the model, `Character` is set to nil,\nand the model is destroyed. No-op when no character is loaded.\n\nRuns automatically when the mock is destroyed or kicked.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 501,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getBackpack",
            "desc": "Returns the mock's current `Backpack` stand-in, or nil before the first spawn. It is a genuine\n`Backpack` parented to the mock, so production code observing its children works unchanged.\n[PlayerMock.loadCharacterAsync] replaces it with a fresh empty one on every spawn:\n\n```lua\nlocal character = PlayerMock.loadCharacterAsync(player, rig)\nlocal backpack = assert(PlayerMock.getBackpack(player))\ntool.Parent = backpack\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Backpack?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 519,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getStarterGear",
            "desc": "Returns the mock's current `StarterGear` stand-in, or nil before the first spawn. It is a genuine\n`StarterGear` parented to the mock, and unlike the Backpack it survives respawns. Consumers that\ndot-index `player.StarterGear` branch:\n\n```lua\nlocal starterGear = if PlayerMock.isMock(player)\n\tthen PlayerMock.getStarterGear(player)\n\telse player.StarterGear\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "StarterGear?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 537,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getPlayerGui",
            "desc": "Returns the mock's `PlayerGui` stand-in, parented at construction. It is really a `Folder` named\n\"PlayerGui\", so consumers branch instead of using `FindFirstChildOfClass`:\n\n```lua\nlocal playerGui = if PlayerMock.isMock(player)\n\tthen PlayerMock.getPlayerGui(player)\n\telse player:FindFirstChildOfClass(\"PlayerGui\")\n```\n\n[PlayerGuiUtils] branches this way internally, so its consumers work against a mock unchanged.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "PlayerGui"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 556,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getPlayerScripts",
            "desc": "Returns the mock's `PlayerScripts` stand-in, parented at construction. Like the PlayerGui stand-in\nit is really a `Folder`, which can never satisfy an `IsA(\"PlayerScripts\")` filter, so consumers\nobserving the child by class branch on the class name:\n\n```lua\nlocal playerScriptsClassName = if PlayerMock.isMock(localPlayer) then \"Folder\" else \"PlayerScripts\"\nRxInstanceUtils.observeLastNamedChildBrio(localPlayer, playerScriptsClassName, \"PlayerScripts\")\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "PlayerScripts"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 573,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "kick",
            "desc": "Emulates `Player:Kick(message)` on a mock, performing the removal sequence rather than merely\nrecording the call:\n\n1. the message is recorded for [PlayerMock.getKickMessage]\n2. the character is removed (see [PlayerMock.removeCharacter])\n3. the mock leaves the DataModel (`Parent = nil`, not a destroy -- a held reference stays\n   readable), so `AncestryChanged` genuinely fires\n\n```lua\nif PlayerMock.isMock(player) then\n\tPlayerMock.kick(player, reason)\nelse\n\tplayer:Kick(reason)\nend\n```\n\n`Players.PlayerRemoving` is a `Players`-service event only the engine fires, so consumers of it\ncannot observe a mock kick -- observe `AncestryChanged` instead.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "message",
                    "desc": "recorded for [PlayerMock.getKickMessage]; nil records \"\"",
                    "lua_type": "string?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 600,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getKickMessage",
            "desc": "Returns the message a mock was kicked with via [PlayerMock.kick], or nil when it was never kicked.\nA kick with no message reads back as `\"\"`. Stays readable after the kick, as long as the caller\nholds a reference.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "string?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 612,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "addReplicationFocus",
            "desc": "Emulates `Player:AddReplicationFocus(part)` on a mock. The backing is a set, so adding a part\nalready focused does nothing.\n\n```lua\nif PlayerMock.isMock(player) then\n\tPlayerMock.addReplicationFocus(player, part)\nelse\n\tplayer:AddReplicationFocus(part)\nend\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "part",
                    "desc": "",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 631,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "removeReplicationFocus",
            "desc": "Emulates `Player:RemoveReplicationFocus(part)` on a mock. The removal half of the branch in\n[PlayerMock.addReplicationFocus]; removing a part that is not focused is a no-op.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "part",
                    "desc": "",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 642,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getReplicationFocuses",
            "desc": "Returns the parts currently focused on a mock, in the order they were added. The engine has no\ncounterpart -- a real `Player`'s focuses can only be added and removed -- so this is the test-side\nreader for [PlayerMock.addReplicationFocus] / [PlayerMock.removeReplicationFocus].",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ BasePart }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 654,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getSignal",
            "desc": "Reads a stand-in native event off a mock: the genuine native signal for events the backing Folder\ninherits from `Instance`, otherwise a signal a test fires through [PlayerMock.fireSignal]. The\npath is validated against the engine's reflection, so a typo errors instead of returning a signal\nthat can never fire.\n\n```lua\nlocal chatted = if PlayerMock.isMock(player) then PlayerMock.getSignal(player, \"Chatted\") else player.Chatted\n```\n\nA bare name reads a `Player` event. A `Service.Event` path reads the mock's own copy of a\nclient-global service event, which a headless server has only one of -- see\n[PlayerMock.getServiceSignal].",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "eventPath",
                    "desc": "`\"Chatted\"` or `\"UserInputService.WindowFocused\"`",
                    "lua_type": "InstancePathTableLike"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 676,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "fireSignal",
            "desc": "Fires the backing signal for an event on a mock, so code connected through [PlayerMock.getSignal]\nobserves the event as if the engine had fired it. Takes the same paths [PlayerMock.getSignal] does.\n\n```lua\nPlayerMock.fireSignal(player, \"Chatted\", \"hello\")\nPlayerMock.fireSignal(player, \"UserInputService.WindowFocused\")\n```\n\nEvents the backing Folder inherits from `Instance` resolve to genuine native signals, which only\nthe engine fires, so they cannot be fired here.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "eventPath",
                    "desc": "`\"Chatted\"` or `\"UserInputService.WindowFocused\"`",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "Event arguments delivered to connected handlers.",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 696,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getServiceSignal",
            "desc": "Reads a stand-in for a client service's event off a mock -- `UserInputService.WindowFocused`,\n`UserInputService.InputEnded`, and the like -- which a test fires through\n[PlayerMock.fireServiceSignal]:\n\n```lua\nlocal localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()\nif localPlayer ~= nil and PlayerMock.isMock(localPlayer) then\n\treturn PlayerMock.getServiceSignal(localPlayer, \"UserInputService.WindowFocused\")\nend\nreturn UserInputService.WindowFocused\n```\n\nNamed sugar over [PlayerMock.getSignal], which takes the same `Service.Event` path -- the mock is\nonly where the backing lives.\n\nArguments cross a `BindableEvent`, so they are marshalled: EnumItems, numbers and strings arrive\nintact, but a table's methods and metatable do not survive. Hand a [PlayerMock.makeInputObject]\nstand-in to [PlayerMock.fireInput] instead when the handler calls methods on it.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "domain",
                    "desc": "a canonical Service.Event, e.g. \"UserInputService.WindowFocused\"",
                    "lua_type": "InstancePathTableLike"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 724,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "fireServiceSignal",
            "desc": "Fires the backing signal for a client service's event on a mock, so code connected through\n[PlayerMock.getServiceSignal] observes the event as if the engine had fired it. Named sugar over\n[PlayerMock.fireSignal].\n\n```lua\nPlayerMock.fireServiceSignal(player, \"UserInputService.WindowFocused\")\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "domain",
                    "desc": "a canonical Service.Event, e.g. \"UserInputService.WindowFocused\"",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "...",
                    "desc": "Event arguments delivered to connected handlers.",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 741,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "bindInput",
            "desc": "Emulates a context-restricted `ContextActionService` call on a mock. The args after the domain are\nthe engine call's own, so a production mock branch is the identical call aimed at the mock:\n\n```lua\nlocal localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()\nif localPlayer ~= nil and PlayerMock.isMock(localPlayer) then\n\tPlayerMock.bindInput(localPlayer, \"ContextActionService.BindAction\", \"Drag\", onDragAction, false, Enum.UserInputType.MouseButton2)\nelse\n\tContextActionService:BindAction(\"Drag\", onDragAction, false, Enum.UserInputType.MouseButton2)\nend\n```\n\nUnbinding goes through the same entry point -- the domain names the operation:\n\n```lua\nPlayerMock.bindInput(localPlayer, \"ContextActionService.UnbindAction\", \"Drag\")\n```\n\nA test dispatches a bound action through [PlayerMock.fireInput]. Deliberately not modelled: touch\nbuttons, priority routing, input-type routing, and the engine's bind stack. All bind domains\nshare one action registry per mock, like the engine's, so rebinding a name replaces the callback.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "domain",
                    "desc": "a known input domain, e.g. \"ContextActionService.BindAction\"",
                    "lua_type": "InstancePathTableLike"
                },
                {
                    "name": "actionName",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "...",
                    "desc": "the engine call's remaining args, e.g. `functionToBind, createTouchButton, ...inputTypes`",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 773,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "isInputBound",
            "desc": "Returns whether the given action is currently bound on a mock via [PlayerMock.bindInput].",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "actionName",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 789,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "fireInput",
            "desc": "Dispatches a bound action on a mock, invoking the bound callback with\n`(actionName, userInputState, inputObject)` -- the engine's argument order -- and returning its\nresult. Errors when the action is not bound.\n\n```lua\nPlayerMock.fireInput(player, \"Drag\", Enum.UserInputState.Begin, input)\n```\n\n`inputObject` is passed by reference, so hand it a real `InputObject`, a plain table of the fields\nthe callback reads, or a [PlayerMock.makeInputObject] stand-in when the callback also needs\n`:GetPropertyChangedSignal(...)`.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "actionName",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "userInputState",
                    "desc": "",
                    "lua_type": "Enum.UserInputState"
                },
                {
                    "name": "inputObject",
                    "desc": "a real InputObject, a plain stand-in table, or a makeInputObject stand-in",
                    "lua_type": "any?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Enum.ContextActionResult?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 812,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "makeInputObject",
            "desc": "Builds a stand-in `InputObject` for [PlayerMock.fireInput] to hand a bound action, for handlers\nthat read more than the raw fields -- in particular `:GetPropertyChangedSignal(\"UserInputState\")`.\nA real `InputObject` is not `Instance.new`-able, so this is a plain table exposing the fields and\nthat one method; drive the press lifecycle with `:SetUserInputState(...)`:\n\n```lua\nlocal input = PlayerMock.makeInputObject({ UserInputType = Enum.UserInputType.Gamepad1, KeyCode = Enum.KeyCode.ButtonA })\nPlayerMock.fireInput(mock, actionName, Enum.UserInputState.Begin, input)\ninput:SetUserInputState(Enum.UserInputState.End)\n```",
            "params": [
                {
                    "name": "props",
                    "desc": "",
                    "lua_type": "InputObjectProps?"
                }
            ],
            "returns": [
                {
                    "desc": "an InputObject stand-in",
                    "lua_type": "table"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 836,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "setSelectedGuiObject",
            "desc": "Sets the mock's stand-in for `GuiService.SelectedObject`, or clears it with nil. A headless server\nhas no PlayerGui, so the engine rejects `GuiService.SelectedObject = obj` outright and selection\ncode branches:\n\n```lua\nlocal localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()\nif localPlayer ~= nil and PlayerMock.isMock(localPlayer) then\n\tPlayerMock.setSelectedGuiObject(localPlayer, button)\nelse\n\tGuiService.SelectedObject = button\nend\n```\n\nNamed sugar over `PlayerMock.write(player, \"GuiService.SelectedObject\", guiObject)`, which reads\nand writes the same per-mock store.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                },
                {
                    "name": "guiObject",
                    "desc": "the focused object, or nil to clear",
                    "lua_type": "GuiObject?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 860,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getSelectedGuiObject",
            "desc": "Reads the mock's stand-in for `GuiService.SelectedObject`, or nil when nothing is selected. The\nread side of the same branch as [PlayerMock.setSelectedGuiObject].",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "GuiObject?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 873,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getSelectedGuiObjectChangedSignal",
            "desc": "Returns the signal that fires when the mock's stand-in for `GuiService.SelectedObject` changes,\nstanding in for `GuiService:GetPropertyChangedSignal(\"SelectedObject\")`.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RBXScriptSignal"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 884,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "setMockedLocalPlayer",
            "desc": "Designates a mock as the local player for the client realm, or clears it with nil. Read back\nthrough [PlayerMock.getMockedLocalPlayer].\n\n```lua\nmaid:GiveTask(PlayerMock.setMockedLocalPlayer(player))\n```\n\nCall this directly *before* booting bags to pre-designate -- matching production, where\n`Players.LocalPlayer` exists before any service runs -- and a booting [PlayerMockServiceClient]\nadopts the designation and owns its cleanup. After boot, designate through\n[PlayerMockServiceClient.SetLocalPlayer] instead.\n\nThe mock must already be parented into the DataModel, the designation being a tag that\n`GetTagged` only resolves for parented instances.\n\nThe returned disposer restores whatever was designated before this call, so nested designations\nunwind correctly. It is a no-op when the designation has since moved on, and calling it more than\nonce is safe.",
            "params": [
                {
                    "name": "player",
                    "desc": "must be a PlayerMock in the DataModel, or nil to clear",
                    "lua_type": "Player?"
                }
            ],
            "returns": [
                {
                    "desc": "Restores the previous designation. Safe to call more than once.",
                    "lua_type": "() -> ()"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 911,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        },
        {
            "name": "getMockedLocalPlayer",
            "desc": "Returns the mock designated as the local player, or nil. This is only ever the mock -- there is\ndeliberately no helper resolving the real `Players.LocalPlayer`, so call sites fall back\nexplicitly and the real read stays visible to luau-lsp:\n\n```lua\nlocal localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Player?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 926,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        }
    ],
    "properties": [
        {
            "name": "TAG",
            "desc": "The CollectionService tag every mock carries, and the channel [PlayerMockService] /\n[PlayerMockServiceClient] discover mocks through. Tag resolution is DataModel-scoped, so a mock\nbecomes discoverable when it is parented in and drops out when it is destroyed or kicked.",
            "lua_type": "string",
            "readonly": true,
            "source": {
                "line": 76,
                "path": "src/player-mock/src/Shared/PlayerMock.lua"
            }
        }
    ],
    "types": [],
    "name": "PlayerMock",
    "desc": "In-memory stand-in for a Roblox `Player`. A real `Player` cannot be `Instance.new`'d and no\nclient joins a headless test place, so a mock is a tagged `Folder` typed as a `Player`.\n\nGuards keep their real-`Player` assert and add an explicit OR clause:\n\n```lua\nassert(player:IsA(\"Player\") or PlayerMock.isMock(player), \"Bad player\")\n```\n\nNative members a Folder cannot expose are read through mock-only accessors, so call sites branch\nexplicitly and the real-`Player` path stays plain member access:\n\n```lua\nlocal player = PlayerMock.new({ UserId = 12345, AccountAge = 30 })\nplayer.Parent = game:GetService(\"Players\")\n\nlocal userId = if PlayerMock.isMock(player) then PlayerMock.read(player, \"UserId\") else player.UserId\n\nPlayerMock.write(player, \"AccountAge\", 31)\n```\n\nEvents follow the same shape through [PlayerMock.getSignal], with [PlayerMock.fireSignal] as the\ntest-side trigger:\n\n```lua\nlocal chatted = if PlayerMock.isMock(player) then PlayerMock.getSignal(player, \"Chatted\") else player.Chatted\nmaid:GiveTask(chatted:Connect(onChatted))\n\nPlayerMock.fireSignal(player, \"Chatted\", \"hello\")\n```\n\nResults of argument-keyed engine calls (group rank, gamepass/asset ownership, ...) go through\n[PlayerMock.writeLookup] / [PlayerMock.readLookup], named by the canonical `Service.Method` and\nkeyed by the arguments the call turns on:\n\n```lua\nPlayerMock.writeLookup(player, \"GroupService.GetRolesInGroupAsync\", {\n\tIsMember = true,\n\tRoles = { { Name = \"Admin\", Rank = 230 } },\n}, 372)\n```",
    "source": {
        "line": 47,
        "path": "src/player-mock/src/Shared/PlayerMock.lua"
    }
}