MockDataStoreService for seamless local development & testing with datastores

datastores

#1

This is an open-source module that emulates datastores in Lua rather than using the actual service. This is useful for testing in offline projects / local place files with code/frameworks that need to have access to datastores.

The MockDataStoreService behaves exactly like DataStoreService – it has the same API, and will also work even if the place is not currently published to a game with Studio API access enabled (it will act as a DataStoreService with no data stored on the back-end, unless you import some data from a json string manually using the module).


Usage:

local MockDataStoreService = require(the.path.to.MockDataStoreService)

-- Use as actual MockDataStoreService, i.e.:

local gds = MockDataStoreService:GetGlobalDataStore()
local ds = MockDataStoreService:GetDataStore("TestName", "TestScope")
local ods = MockDataStoreService:GetOrderedDataStore("TestName")

local value = ds:GetAsync("TestKey")
ds:SetAsync("TestKey", "TestValue")
local value = ds:IncrementAsync("IntegerKey", 3)
local value = ds:UpdateAsync("UpdateKey", function(oldValue) return newValue end)
local value = ds:RemoveAsync("TestKey")
local connection = ds:OnUpdate("UpdateKey", function(value) print(value) end)

local pages = ods:GetSortedAsync(true, 50, 1, 100)
repeat
	for _, pair in ipairs(pages:GetCurrentPage()) do
		local key, value = pair.key, pair.value
		-- (...)
	end
until pages.IsFinished or pages:AdvanceToNextPageAsync()

local budget = MockDataStoreService:GetRequestBudgetForRequestType(
	Enum.DataStoreRequestType.UpdateAsync
)

-- Import/export data to a specific datastore:

ds:ImportFromJSON({ -- feed table or json string representing contents of datastore
	TestKey = "Hello world!"; -- a key value pair
	AnotherKey = {a = 1, b = 2}; -- another key value pair
	-- (...)
})

print(ds:ExportToJSON())

-- Import/export entirety of MockDataStoreService:

MockDataStoreService:ImportFromJSON({ -- feed table or json string
	DataStore = { -- regular datastores
		TestName = { -- name of datastore
			TestScope = { -- scope of datastore
				TestKey = "Hello world!"; -- a key value pair
				AnotherKey = {1,2,3}; -- another key value pair
				-- (...)
			}
		}
	};
	GlobalDataStore = { -- the (one) globaldatastore
		TestKey = "Hello world!"; -- a key value pair
		AnotherKey = {1,2,3}; -- another key value pair
		-- (...)
	};
	OrderedDataStore = { -- ordered datastores
		TestName = { -- name of ordered datastore
			TestScope = { -- scope of ordered datastore
				TestKey = 15; -- a key value pair
				AnotherKey = 3; -- another key value pair
				-- (...)
			}
		}
	};
}

print(MockDataStoreService:ExportToJSON())

There is also a small helper module provided (“DataStoreService”) that automatically detects and selects which datastores should be used (real datastores for published games with API access, mock datastores for offline games / published games without API access).


Features:

  • Identical API and near-identical behaviour compared to real DataStoreService.
  • Error messages due to invalid input parameters are more verbose/accurate than the ones generated by actual datastores, which makes development/bug-fixing easier.
  • Throws descriptive errors for attempts at storing invalid data, telling you exactly which part of the data is invalid. (credit to @Corecii’s helper function)
  • Emulates the yielding of datastore requests (waits a random amount of time before returning from the call).
  • Extra API for json-exporting/importing contents of one/all datastores for easy testing.
  • All operations safely deep-copy values where necessary (not possible to alter values in the datastore by externally altering tables, etc).

Not yet implemented: (pull requests welcome!)

  • Currently, datastore budgets are never reduced when requests are made, and requests will never error or cache because of an exceeded request limit.
  • Does not enforce the “6 seconds between writes on the same key” rule yet.
  • Does not respect diabolical mode in Studio (i.e. randomly fail requests for the purpose of testing) since the setting for this mode cannot be read. Custom diabolical mode for this module in the future maybe?

Get the module:

Download the module directly:
MockDataStoreService.rbxm (10.4 KB)

Or grab the module from the library:

Or get the source code here: (and submit PRs/issues!)


Feel free to leave questions, comments, requests for improvements, and bug reports here, or as issues on the GitHub repository!


#2

This is neat for CI testing, I like it!


#3

This is awesome, thanks!


#4

Thanks for the contribution. This is awesome!


#5

It would be good to simulate the request limits for the data store as well.


#6

Need some ice for that burn

Thank God this exists though. I was so annoyed I was no joke gonna make one, arguably of worse functionality, at home. Wonderful.


#7

As you can see in the OP, that’s already on the TODO list. It was a non-trivial amount of work to implement so I left it out for v1. If you want you can file a pull request for it if you can manage to edit the code in a clean way to support it!