DataStoreMock
In-memory stand-in for a Roblox GlobalDataStore used by tests. It faithfully
round-trips values through a deep copy (mimicking JSON serialization, so aliasing
bugs surface the same way they would against a real datastore) and lets tests inject
failures such as the 509 Personal-RCC block.
It is a first-class citizen of the datastore package: the DataStorePromises wrappers
accept it anywhere a real datastore Instance is expected via
DataStoreMock.isDataStoreMock.
local store = DataStoreMock.new("PlayerData", "SaveData")
store:SetRaw("key", { coins = 5 })
-- Simulate Roblox datastores being down
store:FailAllRequests(DataStoreMock.OPERATION_NOT_ALLOWED_509)
Properties
OPERATION_NOT_ALLOWED_509
DataStoreMock.OPERATION_NOT_ALLOWED_509: stringThe error Roblox raises when datastore operations run on a Personal RCC. This is the real-world failure that motivated the mock.
MAX_VALUE_LENGTH
DataStoreMock.MAX_VALUE_LENGTH: numberThe per-key serialized-value ceiling Roblox enforces (4 MB). Real datastores serialize a key's whole value to JSON and reject the write when that blob is larger than this, which is how a save fails once too much data accumulates under one key. Pass this (or a smaller value, to trigger it without a multi-megabyte payload) to DataStoreMock.SetMaxValueLength.
Functions
isDataStoreMock
DataStoreMock.isDataStoreMock(value: any) → boolean
Returns whether the given value is a DataStoreMock. Used by DataStorePromises so the
mock can stand in for a real datastore Instance.
new
Constructs a new DataStoreMock.
SetYieldTime
Sets how long (in seconds) each request yields before completing, to mimic real datastore latency. Defaults to 0 (no yield) so tests stay fast.
SetMaxValueLength
Enforces a serialized-value ceiling on SetAsync/UpdateAsync, mirroring the way real
datastores reject a write once a key's whole value serializes past their per-key size limit.
A write whose value JSON-encodes to more than maxValueLength bytes throws (and stores nothing),
so tests can exercise the overflow-save failure path without a multi-megabyte payload. A value the
mock cannot serialize at all throws the same way a real datastore rejects non-UTF-8 data.
Pass DataStoreMock.MAX_VALUE_LENGTH for the real 4 MB ceiling, a smaller number to trigger it cheaply, or nil to disable the check (the default, so existing tests are unaffected).
SetErrorInjector
DataStoreMock.SetErrorInjector(errorInjector: ((ErrorInjectorContext) → string?)?) → ()Injects a callback consulted before every request. Returning a string from the callback makes that request throw the string as its error; returning nil lets the request proceed.
FailAllRequests
DataStoreMock.FailAllRequests(errorMessage: string?--
Defaults to the 509 Personal-RCC error
) → ()Makes every subsequent request throw the given error until DataStoreMock.StopFailing is called. Simulates a total datastore outage.
FailNextRequests
DataStoreMock.FailNextRequests() → ()
Makes the next count requests throw the given error, then recover. Simulates a
transient outage that the retry logic is expected to survive.
StopFailing
Clears any injected failures.
BlockRequests
Makes every subsequent request hang (yield) inside the datastore call until DataStoreMock.UnblockRequests is called. Simulates a request that does not settle -- e.g. a lock command that can take up to ~30s to propagate across servers -- so tests can exercise a request in flight (and its maid cancelling the yielding thread).
UnblockRequests
Releases requests blocked by DataStoreMock.BlockRequests. A request whose thread was cancelled while blocked never resumes.
GetCallCount
DataStoreMock.GetCallCount(method: string?--
e.g. "GetAsync", "UpdateAsync"
) → numberReturns the number of times a given API was called (or total across all APIs when no method is given). Failed calls count too.
SetRaw
Directly seeds a stored value without datastore semantics (no version bump, no failure injection). For test setup.
GetRaw
Directly reads a stored value without datastore semantics. For test assertions.
ExportRaw
Serializes the full raw key -> value store to a JSON string, so a mock's contents can survive between two in-process "server sessions" (e.g. an integration test simulating a cross-place teleport) or be written out as an inspectable, diffable checkpoint artifact. Datastore values are JSON-safe by contract, so the export is lossless; hydrate a fresh mock from it with DataStoreMock.ImportRaw.
Errors when the store holds a value that cannot be JSON-encoded (which a real datastore would have refused to store in the first place).
ImportRaw
Decodes a JSON string produced by DataStoreMock.ExportRaw and replaces the store contents with it. This replaces rather than merges: every existing key is discarded, along with its version/userId/metadata bookkeeping. Each imported key is then seeded exactly like DataStoreMock.SetRaw (no version bump, no failure injection), so a hydrated mock is indistinguishable from a fresh one seeded key-by-key.
GetAsync
Mimics GlobalDataStore:GetAsync.
SetAsync
DataStoreMock.SetAsync() → string--
version
Mimics GlobalDataStore:SetAsync.
UpdateAsync
DataStoreMock.UpdateAsync() → (any,any)--
value, keyInfo
Mimics GlobalDataStore:UpdateAsync. The transform receives the current value and a
key-info stand-in, and returns newValue [, userIds [, metadata]]. Returning nil cancels
the update (matching Roblox semantics).
RemoveAsync
Mimics GlobalDataStore:RemoveAsync.
IncrementAsync
Mimics GlobalDataStore:IncrementAsync.