How do you test a text adventure? Is manual testing the only way or could you automate some testing?
When creating Bradford Mansion, I wanted to test certain aspects of the game. In this article I will write about what kind of test mechanics I developed for the game / engine. If you are not into text adventure game creation or software development, then you should just click the link above to read about the game itself 🙂
While automating testing is important, I still needed to beta test of course. The game was played by several people who provided valuable feedback. But my automated testing also helped me find bugs before I even had sent them out to testers.
Getting to a certain point in the game
As I started creating the game itself, a problem arose: I always needed to type a lot of commands to get to a certain point in the game.
I have decided early on that saving and loading game state would be implemented only later on. Saving also did not always work, because I changed some aspects of the engine that broke the save game files. I needed some way to get to a certain point in the game.
The first step of my automated testing was just writing commands in a separate file, that the engine could then play before giving input to the player.
These were the same commands a user would enter:
>southCommands from the beginning of the game
This solved my problem of having to type a lot. As I was progressing in creating the game I also added the commands needed to reach that point. So then I always needed to just continue from that point forward.
Stopping the flow
But then sometimes, I needed to get back and do something at another point in the game. I also wanted to see inside the script at what point I was in the game. I added two things to my ”automate” script.
- If a line would contain a . by itself on a line, the automation would stop.
- If a line would begin with the # symbol, it is treated as a comment
So now I could stop the execution at any point I wanted to by simply adding a . in the file. It could then be removed or moved around to check out various points in the game.
The comments allowed me to quickly find places in the growing automation script.
This simple automation was used almost to the end of the game development cycle. In the end, the script contained the ”walkthrough” for the game. I could run the game with the script and see that everything still works.
Sometimes I would add a change that would break the game, and just running this script would show this to me.
Order of things, strange commands
Having this type of testing did not solve all of my problems though. It only tested the game in a linear fasion with a single set of commands. If the player would proceed in a different order, that might still be a problem.
This method also did not allow me to test all of my various verbs and commands. Based on feedback I added synonyms and alternate usages to items, and I wanted to be able to test for those as well.
Alternate commands and looping
I needed a way to test all of my verbs. So I introduced the | operator for the script. If the line would contain a | symbol, it would be used to break the line up into many commands, and the engine would then execute one on random.
I could now say: south | s | enter the door | enter mansion | in | go south
This line contains 6 things that end up with the same result. Every time this line would be executed, the engine chooses one of these commands. It took some time to update the entire script with all sorts of alternative commands.
I also added a loop mode, where the automation script would be executed in a loop (well actually a single run would loop 100 times), and after each loop the game would be reset. This allowed me to more thoroughly test command combinations where a different set of commands were executed in every run. After a change to how something behaves, running the looped testing a few times often broke the game – and thus allowed me to fix it before even giving out a new version to testers.
I then decided to add meta commands so I could really test the state of the game. I decided my debugging commands would begin with a 0 (zero). These are all disabled in the final game (release compiliation).
- 0assertscore x – this would check if the current score was x. It could be used to verify that an action did perform as I intended to.
- 0assertflag flagname or !flagname would check if a global flag was set. This could be used to see if some action was triggered after doing something.
- 0asserthas itemname – check if the player is carrying an item
- 0assertdoesnothave itemname – check that the player was not carrying an item
- 0assertlocked doorname – check if a door is locked
- 0assertunlocked doorname – check if a door is unlocked
This allowed me to write tests such as:
- Verify the player does not have item X
- Close the container of item X
- Excecute command take X
- Verify the player does not have item X
- Open the container of item X
- Execute command take X
- Verify the player has item X
This would be used to verify my container logic is working. Similar tests could be written for checking door states, or if the player has found some things, seen things, etc. Some of these tests are more engine testing than game testing, but it helped a lot during development.
This found bugs where items were taken that were not supposed to be taken before certain things happened, or should not have been visible without completing certain things.
(The 0 at the beginning of these commands was used because I also have other debugging commands, some of which is similar to the regular verbs, and these also get the 0 at the start, for example 0give would give the player any item in the game.)
Do I really need all this for an adventure game?
That is a good question. Probably a some of these tests that I have been writing would not need to be done with a “normal” adventure game. But for me, I have been creating my engine at the same time as the game. This meant I sometimes broke things in the game, and having tests like these made me more comfortable changing things.
Other tests, like the score testing was used to verify that all goals will give the intended result, and that the total is what I expected it to be at the end of the game.
All this testing code really payed off in the end, because I could verify that everything is working as intended. I could also progress the game to any puzzle or state and verify alternate verbs or test a certain bug.
When I added save and load commands, I could add save points in the script – so I could generate save games at different points in the game (at the end of the acts, because the story was partitioned into three acts).
It’s a very useful tool going forward. As I add features to the engine and start working on my next game, I can already confidently test that I don’t break anything and things work as they are supposed to work.