Testing multiplayer
A successful solo run proves less than it seems. It does not show that two players agree on a score, that both can touch the same collectible safely, or that a departing player leaves no broken references. This lesson turns the co-op project into a small, repeatable test exercise.
Prerequisites: A saved copy of the co-op rounds project, with its server-owned target and visible status. Understand which values the server owns. Keep any optional controls lab separate unless you deliberately integrated it.
Outcome: A short test record that says what was tested, what actually happened, and what remains unknown. Do not label something “passed” merely because no error appeared.
Start with the right simulation
Studio currently offers Test, Test Here, Run, and Server & Clients. Test includes your avatar; Run does not. A solo test runs client and server simulations that you can inspect separately. For multiplayer, choose Server & Clients, select 2 clients, and start the session. Use End Session when finished. Menu labels and layout may evolve; use the visible testing controls if your Mac's function keys operate brightness or other system features. See Studio testing modes.
Use two windows as two separate players. Give them simple labels in your notes: Client A and Client B. Do not confuse the server view with a third playable character. Keep Output available and inspect the origin of each message.
Write expectations before playing
Create a record with these headings: build/date, test mode, case, expected result, actual result, pass/fail/untested, and evidence. A screenshot is useful for a clipped label; a copied error with script path and line number is better for an exception. Avoid including account information in a public bug report.
Run these cases against the exact rules in lesson 12:
- Fresh session: Both players begin in a sensible place. The same shared target and phase are visible to both clients.
- One pickup: Move A to one collectible. Both clients eventually display the same new total. B should not receive an unrelated personal reward.
- Contested pickup: Move A and B onto the same collectible nearly together. One collectible must not count twice.
- Round completion: Reach the target. The server chooses the transition once; both clients see the same result.
- Next round: Wait for restart. Collectibles and counters reset according to the project rules, with no duplicated objects or growing event behavior.
- Respawn: Reset A's character during play. B can continue, A receives a new character, and shared progress follows the documented round rules.
- Departure: Close one simulated client. The remaining client can continue and the server logs no stale-character exceptions.
- Repeated sessions: End the session, start fresh, and repeat the decisive cases. A fix that works only after a specific editor history is not yet understood.
The checklist is an original test plan for this manual, not a claim that Roblox automatically verifies these outcomes.
Observe lifecycle events
If you need help distinguishing a player departure from a character respawn, add this temporary Script at ServerScriptService > SessionObserver. It does not change gameplay.
Runnable file: 14-session-observer.server.luau.
-- Placement: ServerScriptService > SessionObserver (Script)
-- Optional temporary diagnostics for the multiplayer test plan.
local Players = game:GetService("Players")
local function reportJoin(player)
print("[SessionObserver] joined", player.UserId)
player.CharacterAdded:Connect(function()
print("[SessionObserver] character spawned", player.UserId)
end)
end
Players.PlayerAdded:Connect(reportJoin)
Players.PlayerRemoving:Connect(function(player)
print("[SessionObserver] leaving", player.UserId)
end)
for _, player in Players:GetPlayers() do
reportJoin(player)
end
Notice that CharacterAdded can happen several times for one Player. That is why a reference captured when the first character appeared can become stale. Remove or disable this diagnostic script when it has answered your question; verbose logging can bury more useful signals.
Test conditions that your laptop hides
Use Device Simulator to inspect a narrow phone layout. Use Controller Emulator to navigate without a mouse. Simulation is useful for input and layout, but it does not prove real-device memory usage, sustained frame rate, battery behavior, or every operating-system interaction. Schedule a real-device check before claiming support for that device family. Roblox testing tools and Controller Emulator describe the supported controls.
Add latency through Studio's Network Simulator and repeat the contested-pickup and completion cases. Look for delayed but consistent feedback, duplicated rewards, and interfaces stuck in a permanent “waiting” state. Record the chosen profile or settings so someone else can repeat your test. Start with the controls available in your installed Studio; do not paste undocumented network-setting commands from an old tutorial.
For later automation, Roblox documents StudioTestService for scripted multi-client sessions, StudioDeviceSimulatorService for device profiles, and VirtualInput obtained through UserInputService:CreateVirtualInput(). These are Studio testing facilities, not ordinary gameplay APIs. This lesson does not require installing a plugin or writing a test runner. Scripted testing overview
Diagnose a failure rather than hiding it
If A and B disagree, inspect the server's canonical state first. Then inspect the replicated value on both clients. Finally inspect how each UI reads that value. This separates a rules bug from replication timing and from a display bug.
If a touch counts twice, check the server's claim/debounce logic. Adding a client-only cooldown may make one screen look better while leaving the shared exploit intact. If a round freezes when A leaves, search for cached Player or Character references and waits that depend on the departed player.
Reduce the case to the smallest reliable sequence. “Collect coin 3 with both players just before timeout” is actionable. “Multiplayer is flaky” is not.
Exercise and completion check
Intentionally introduce one harmless display bug in a copy, such as showing the wrong target in a label. Have another tester report it without seeing the code. Use only their reproduction steps to locate it, then restore the correct code and rerun the same case.
You are done when the eight cases have honest results, any failures have reproducible steps, and device/network tests are marked tested or explicitly untested. A known limitation recorded clearly is more useful than an invented green checkmark.
Verification: Official testing documentation checked 2026-10-03. The test plan and optional diagnostic script were reviewed but not run in Studio for this manual.
Previous: 13 Cross-device controls · Next: 15 Saving progress
Related: 10 Remotes and validation · 16 Debugging and performance · Help context