Lesson 11.1: Creating the Unit Test Project

As the game gets more features, it becomes more difficult to manually test. So, we will add a unit test project that can do fast, automated tests for us.

This will help us as we add more features. We can run the unit tests and see if the changes broke any of the pre-existing code.



Lesson Steps


Step 1: Create a new unit test project

Right-click on the SOSCSRPG solution (in Solution Explorer) and choose Add -> New Project -> Visual C# -> Test -> Unit Test Project (.NET Framework)

Name the unit test project “TestEngine”. There is no naming requirement for the solution, but I like to match my unit test project name with the name of the class library project it is testing.

Add reference in the TestEngine project to Engine project.

Delete UnitTest1.cs class – the default file created with project.

Create “ViewModels” folder in TestEngine class. This is not a .NET requirement. It’s just how I like to synchronize my tests projects with the class library project.



Step 2: Create new unit test class TestEngine\ViewModels\TestGameSession.cs

Right-click on the ViewModels folder in the TestEngine project and select Add -> New Item -> Visual C# Items -> Test -> Basic Unit Test. Name the file TestGameSession.cs

Add “using Engine.ViewModels;”, so we can access the GameSession class in the Engine.ViewModels namespace.


On line 6, notice that the unit test class has a “[TestClass]” attribute. This is how the unit test runner knows which classes have unit tests.


On lines 9 through 16 we have a unit test. Each unit tests needs to have the “[TestMethod]” attribute, to identify the function as a unit test. All unit test functions are “public void”.

On line 12 is the logic we are going to test. In this test, all we want to check is that instantiating a GameSession object will also instantiate a Player object, assign it to the CurrentPlayer property, and set the CurrentLocation to the town square.

The tests are the “Assert” statements on lines 14 and 15. These assertions are what we expect to happen, after we run the other code in the test function (in this case, only line 12)

On line 14, we assert that GameSession.CurrentPlayer should not be null. When the test runs, if this assertion is false (CurrentPlayer is null), then the test will fail.

On line 15, we assert that the CurrentLocation.Name should be “Town square”. If that assertion is not true, the test will fail.

If our assertions are correct – the values are what we expect – the test passes.


In the test on lines 18 through 27, we instantiate a GameSession object and apply 999 hit points of damage to the player. This should kill the player, causing them to move to their home location and completely heal (the things we test in the assertions on line 25 and 26).

This test exposed a problem. The GameSession.OnCurrentPlayerKilled function assumes the player was always killed by the CurrentMonster (line 293, where it displays the CurrentMonster.Name value).

We saw this type of problem before – when we had to put our RaiseMessage calls before the code that could change objects.

For now, let’s change line 293 of GameSession.cs to say, “You have been killed”. In the future, we may add other ways the player could be killed: poisons, spells, curses, etc. But, we will worry about making changes later, if we actually add those changes.







Return to main page

11 thoughts on “Lesson 11.1: Creating the Unit Test Project

  1. Hey Scott!  Another great lesson,  This will really help us as we get further and further along.  You mentioned in the lesson that it was possible to set up our source control to kick off our tests when we commit changes.  How does that work?  I looked around for some TortoiseSVN documentation that might show how to do that.  Would that be something that you could really only set up in a production environment?

    1. Thanks, Gary. Automatically running your unit tests on check-in is called “continuous integration”. This is something you’d do in the development environment. You’d want to know about problems before you deploy the program to the production environment. In the past, I’ve used TeamCity. Another popular one is Jenkins. There are other options available on GitHub.

      I’m not sure yet which one I’ll use.

  2. Hello Scott,

    You are doing great work here! I was just curious if you planned on adding mocks to your lesson as well?

    1. Thank you. Yes, I’ll be adding mocks when we create the persistence (XML and SQL) for the game data (items, monsters, locations, etc.) and the player’s saved game data. I may also create something for the random number generator, so we can test things that use it – like the weapon damage.

  3. Hi Scott,
    Thank you for this tutorial. I learn a lot and I’m already planning my own game. Something is going wrong though, because I can’t get the test project to do its work. The “Run Unit Test” option in your video is not there.



    I think I did exactly what you showed in the video and this page, but I can’t get it to work. Any idea?

    1. You’re welcome Tommy,

      I think the “Run Unit Test” option I used was from the ReSharper plugin. I’ve used it for so many years, it’s difficult to remember what is part of Visual Studio, and what is part of ReSharper.

      You should have a “Test” menu option at the top of Visual Studio. Click on it and select Run -> All Tests.

      Visual Studio menu option to run unit tests

      Let me know if you don’t have that menu option available.

  4. I don’t have access to the videos, the country I reside in isn’t friendly with YouTube, so I apologize if you covered this in the video.

    I’m curious about what I should be testing, if I were to create additional tests on my own. I assume that creating hundreds or even thousands of tests for every aspect would be the same as manually testing everything. Is there a rhyme or reason to what I should automate, and what I should do manually later?

    1. Hi Jie,

      Usually, you only need to create a few tests for each function. It’s good to have a test for each “path” through a function. For example, you only need one test for this function:
      public int AddTwoNumbers(int a, int b)
      return a + b:

      However, this function has two “paths”: one where the condition is true (the “a” parameter is zero or higher), and one where it is false (the “a” parameter is lower than zero):
      public string NumberIsPositive(int a)
      if(a >= 0)
      return "Yes";
      return "No";

      You might also want to have a unit test for “boundary conditions” or “edge cases”. For example, what if you pass in a “null” for a parameter? What if you pass an empty string into a function that expects a string? What if you pass a negative number into a function that calculates a square root (which cannot be done for a negative number)? Some of these conditions are difficult to think of, and that is the source of many bugs in programs – handling unexpected values. I don’t know a good source to find all the “edge cases”. It is mostly experience that helps a developer think of everything that can go wrong in a function. When you have a bug in one program, you remember to check for that condition in your future programs.

      But, it is best to try to automate as many tests as you can. Most of the programs I work on have tens of thousands, or hundreds of thousands, of lines of source code. It would take hours (or days) to manually test every possible path through those programs.

      Let me know if you still have questions.

  5. Scott,
    To pass the unit test and still see name of the monster that killed you is the following code legitimate?

    snip from GamesSession.cs


    1. You could try that. I think that’s what I originally had, but some of the events were handled in an order that caused problems. That’s something to watch out for with event-driven programming. Event “A” may process before event “B”, and “A”‘s code may change some property or variable that “B” is depending on.

      Try adding this to your code, to see what happens.

Leave a Reply

Your email address will not be published. Required fields are marked *