Verdict
Yes
Yes, write one or two unit tests for the core rule of a game, such as its damage or scoring calculation, and playtest the movement, visuals and feel instead of testing them.
Why
- Blast radiususers
- Change frequencyconstantly
- Detectabilitysame-day
- Reversibilitywith-effort
- Test costheavy
Yes, write one or two unit tests for the core rule of an indie game and playtest the rest. In the typical case, a game in Steam Early Access, Blast radius is users because players hit each bug, and Change frequency is constantly because a patch ships in most weeks. Detectability is same-day: players report a visibly broken mechanic on Discord within a day. Reversibility is with-effort: a fix means a new Steam build, and players keep manual save slots to fall back to. Test cost is heavy: in a typical Unity or Godot project the rules sit in engine scripts that read scene state, so a test needs a loaded scene or an extraction first, and rule R12 gives Test minimally.
| When | Decision | Why |
|---|---|---|
| The rules already live in plain classes that take numbers and return numbers | Test each rule and its likeliest edge cases in edit-mode tests in CI | Test cost falls to moderate: a rule test needs no scene and survives each retune |
| A change to movement, camera or animation shows on screen each time you press Play | Do not test; play the scene after each change | Detectability becomes immediately: the wrong jump is on your screen before you commit |
| The open question is whether a level is fun or the difficulty curve is right | Test it differently: playtests, telemetry on where players quit, and a Steam beta branch | Test cost rises to prohibitive: no assertion can tell whether a jump feels good |
| Players keep save files across updates, and the game auto-saves over the old file | Test the save and load round trip and loading a save from each shipped version | Reversibility rises to impossible: an auto-save over a broken file loses progress for good |
| The game sells in-game currency for real money | Test mandatory: the amount credited per purchase and a receipt that arrives twice | Blast radius rises to money and Reversibility to costly: a wrong credit ends in refunds |
| A game jam entry that nobody changes after the jam | Do not test; play the build once before you upload it | Change frequency falls to once and Reversibility to trivial: a new upload replaces the old one |
What breaks if you don't test
A broken core rule reaches every player in the next patch. A refactor makes armour cancel all damage, the final boss becomes unkillable, and players post on Discord within hours. The fix is a new Steam build that evening and a reply to each thread; one unit test on the damage rule would have failed before the upload.
What you lose if you over-test
A play-mode test that asserts the player lands at x = 4.2 after a jump fails each time the designer tunes gravity, and someone updates the number without asking whether the jump got worse. An article on what not to unit test in games argues the same: views change more often than logic.
How to test
- Move the core rule, such as damage or scoring, out of engine scripts into a plain class that takes numbers and returns numbers.
- Write one edit-mode test for it with inputs you can work out by hand. Once all the rules live in plain classes, test each rule and its edge cases. The Unity Test Framework runs edit-mode tests without entering Play mode; GUT does the same for Godot.
- Run the tests in CI on every push and turn each reported rule bug into a regression test.
- Before each release, push the build to a Steam beta branch and load a save slot made on the previous version.
When the answer changes
- The game auto-saves over the player's only save file: test the save and load round trip and loading a save from each shipped version.
- The game sells anything for real money: the purchase path needs mandatory tests.
Real incident + Code example
The patch that emptied inventories
On an Early Access survival game I worked on, patch 0.7 renamed the item field count to quantity. The loader skipped unknown keys, so every item in a 0.6 save loaded with a quantity of zero, and the game kept one auto-save file per player and wrote over it at the first checkpoint. We shipped a fix that evening, but about 200 players had lost their progress for good. I had playtested the patch only from fresh saves. The first test below fails on the broken build; the second catches a writer and loader that disagree:
using NUnit.Framework;
public class SaveCompatibilityTests
{
// Save files written by each shipped build, kept in the repository
[TestCase("Saves/v0.6.json")]
[TestCase("Saves/v0.7.json")]
public void OldSaveKeepsItemQuantities(string path)
{
var save = SaveLoader.Load(TestFiles.Read(path));
Assert.That(save.Inventory[0].Quantity, Is.EqualTo(35));
}
[Test]
public void SaveAndLoadKeepItemQuantities()
{
var game = TestFiles.SampleGame(); // slot 0 holds 35 wood
var loaded = SaveLoader.Load(SaveWriter.Write(game));
Assert.That(loaded.Inventory[0].Quantity, Is.EqualTo(35));
}
}
Related questions
FAQ
- Do game developers write unit tests?
Many game developers unit test only game logic, such as rules and save files, and check movement and feel by playing. Unity ships a test framework for such tests, and Godot developers use the GUT add-on.
- What should I unit test in a Unity game?
In a Unity game, unit test the rules that turn numbers into numbers, such as damage, scoring and save file loading, in edit-mode tests. Leave movement, camera and animation to playtesting, because their tests break at every tuning pass.
- How do I test save files in a game?
Test save files with two unit tests: one that saves and loads a sample game and compares the result, and one that loads a save file from each shipped version, kept in the repository. Run both in CI on every push, so a patch that breaks old saves fails before the upload.