Last updated on December 31, 2016
I just posted a video on how to create some basic unit tests, using Visual Studio Community 2015 and MSTest (the unit test library built-in to Visual Studio).
Why create unit tests?
Sometimes, when you change one part of a program, it causes an error in a different part. This is especially true with large programs. Parts of the program are often connected in ways you don’t always immediately see.
Some programmers try to manually test their program after each change. However, this can take a long time, and they need to be sure they don’t forget any of the tests.
You can solve many of your testing problems by writing automated unit tests.
Unit tests are small functions you write, inside a special test project in your solution, that test the other non-UI classes in your program. The great thing about them is that you can run these unit tests very quickly and easily – every time you make a change to your program.
What we’ll use to create unit tests
There are different tools you can use to create unit tests in Visual Studio (NUnit and xUnit.net are two popular libraries). I used the built-in MSTest library in this video. Fortunately, most of the popular unit test libraries work the same way. So, if you learn one, you can easily learn to use another.
What we are going to test
We’ll test a Player class for a role-playing game. These are the situations we want to test:
- When the player takes damage, their current hit points decrease
- The player’s current hit points can never be below zero
- If the amount of damage is a negative number, do not apply it to the player’s current hit points.
Can you use unit tests to test everything?
It’s difficult to write unit tests for the user interface. There are some ways to do it, but they are usually more difficult to setup. It’s much easier to write unit tests for classes in a class library. That’s why I try to move most of the “logic” to the classes in a class library – it’s easy to write unit tests for that type of code.
It’s also difficult to write unit tests for classes that use “external resources”, such as databases, the file system, web services, etc. The problem is that you can’t be 100% sure what is in that external resource – or if it’s working. If your database server is not running, then your unit test will fail, even if your code is working correctly.
You can use “dependency injection” and “mock objects” to test your code that uses external resources. I plan to make some more guides soon on how to do that.
Video transcript
Hello again, .net developers. In this video I’ll show you how to create unit tests. We’ll use Visual Studio Community 2015 and the MSTest library which comes automatically with Visual Studio.
There are some other unit test libraries you can download and use, like NUnit and xUnit. These other libraries give you some more features and you probably are going to want to use them for your real programming, but they all work about the same so we’ll do the demonstration with the built in MSTest.
You may be asking, what’s he gonna test.
When you create a program, you have functions in your classes contain all the logic for our immunity test. Call this function and make sure they return the results you expect.
For example, let’s say you have a Calculator class with an AddTwoNumbers() function. If you call that, passing in one and two, you expect it to return three. So you could create a unit test that does that. It calls your calculator function and checks to make sure it gets the right result.
Unit tests are extremely useful in large programs or if you’re working with other developers. Part of the program that breaks something in a completely different area that you didn’t realize was connected, and a unit test will let you know about that right away.
In this video, we’ll write some tests for a player class for a pretend role-playing game.
The player class has a CurrentHitPoints property and we have a ReceiveDamage method. So when the player is fighting, if a monster hits him for some damage, we subtract that from the player’s CurrentHitPoints.
Here’s our sample solution, with a class library called Engine. This is where we’re going to put in our Player class.
Normally, your unit tests are going to be run against your class libraries. Instead of a user interface or the database, or the file system, anything like that. You want something that does not depend on anything else, doesn’t depend on the user clicking a button, doesn’t depend on value on a database or anything Make sure that the function works the way it’s supposed to.
Here we have our Player class and our constructor, we pass in the number of hit points for the player, save that to the CurrentHitPoints property, enter the received damage function, where you pass in the amount of damage and we subtract that from the current hit points.
There are a couple extra rules we want to apply to the ReceiveDamage function. If we pass in a negative number for the amount of damage, we want to ignore that. If we pass in more hit points of damage than the player has left, for example, the player has 10 hit points and receives 16 hit points for damage, we don’t want them to have a negative CurrentHitPoints. Instead, we want it to be 0.
Now we’re going to create a test project that will hold all our unit tests.
Right-click on the solution, select “Add New Project”, and in the Project Type list, select Test -> Unit Test project. I’m going to name this TestEngine, because I like my unit test project name to match up with the class library it’s testing.
This creates a TestEngine project with a sample unit test class.
The first thing you need to do is right click on the references in your test project, and add a reference to the class library you’re going to test. Otherwise, it can’t see the Player class.
I’m going to rename the unit test class to TestPlayer, since we’re going to put our unit tests for the Player class in here.
Okay, now we’re in our TestPlayer class. The first thing we need to do is add:
using Engine;
So we have a reference to the name space with the Player class in it.
When we look at the TestPlayer class, we see it has [TestClass] attribute in front of it. This is how your testing tools know which classes have tests in them.
Our method has a [TestMethod] attribute in front of it. Again, so the test tools know “here is a unit test”.
For this first test I’m gonna rename it TestHitPointsAreSubtracted, and I’m going to say:
Player player = new Player(10);
This will create a Player object for us. And I’m gonna say:
player.ReceiveDamage(3);
So, at this point the player should have seven current hit points remaining.
Next is where we create our assertion, this is the actual testing part of the unit test.
So you type Assert, and Intellisense shows that you have a whole list of things you can do here: are equal, are not equal, are the same, is false, is true, etc. In our case we are going to use AreEqual.
Assert.AreEqual(7, player.CurrentHitPoints);
What this unit test will do is create a player with ten hit points, give them three points of damage, and then check to see that the player’s current hit points are equal are equal to seven.
If that’s true then the unit test will pass, if it’s false then the unit test will fail.
In order to run a unit test, you go up to the “Test” menu option in Visual Studio, select “Run”, and say “Run all tests”. And now the test explorer pops out. I’ll pin this, and we see that our test here, TestHitPointsAreSubtracted, is green, so it’s good.
Just to show you what would happen if there was an error, we’ll say we expect six hit points in the Assert.
So if I go “Test” -> “Run all tests”, then we see there’s a red x, and if we look at the bottom of the test explorer we see, “Expected a six, but it actually got a seven”.
So let’s put that back to seven.
We’re gonna test again, to make sure it works. And we’re back to green.
So now we’ll add some more tests for our other situations.
It’s just like creating a function in any other class, except you have to remember to put the [TestMethod], in front of it and make it a “public void” function.
We’ll create a test method named TestNegativeDamageDoesNotChangeCurrentHitPoints. Because if you pass in a negative amount of damage, we don’t want that to affect the player’s CurrentHitPoints.
We’ll create a new player object again, with ten hit points. And we’ll use:
player.ReceiveDamage(-3);
We create a new player with ten hit points. We say he receives negative three damage, so we’re expecting the player’s current hit points to still be ten.
Go back up to test, on the menu, and run all tests.
We see our first test succeeded, but this one failed. The error message is “Expected a 10”. That’s because we haven’t made anything in the player class handle negative damage.
Go back to our player class. We’ll say if points of damage less than zero, it just skips out of there and doesn’t apply the damage.
We save that, and go back to test run all tests.
Now, both of them are green.
This is doing called test driven test driven development. That is where you put in your new requirement as a unit test first and then you go back and change your code, so your tests all pass.
Some people like this. Some people think you end up writing too much testing code and not enough real code.
In my opinion, you really need to find a balance for how complex you program is, and how many people are working on it. But it is nice to have these around, because if you make a change a year from now, you can run all your tests and be pretty sure that your change didn’t break anything.
So let’s add another test.
This one will be:
public void TestMoreDamageThanCurrentHitPointsEqualsZero()
So, for this test, we create a player with 10 hit points. We do 13 points of damage to him. We want him to have 0 hit points. We run our test and see that this one failed. We expected zero but we actually got negative three.
Again, this is because we don’t have code that can declare that damage function to deal with this.
So we go back to the player ReceiveDamage function and put in another quick and easy solution:
if(CurrentHitPoints < 0) { CurrentHitPoints = 0; }
We go back to our tests and run them all. And now they all pass.
That’s the basics for creating unit tests in Visual Studio with the MSTest library. These were pretty simple tests but they should give you an idea of the concept.
In the next video I’ll show how you can start doing continuous integration.
Whenever you save changes to your source control, your continuous integration program will automatically build your solution and run all of your unit tests – to let you know if anything failed.
This is really nice with big systems – especially when you’re working with a dozen other programmers. You know right away that something broke so you can fix it.
SOURCE CODE
Player.cs
namespace Engine { public class Player { public int CurrentHitPoints { get; private set; } public Player(int hitPoints) { CurrentHitPoints = hitPoints; } public void ReceiveDamage(int hitPointsOfDamage) { if(hitPointsOfDamage < 0) { return; } CurrentHitPoints = (CurrentHitPoints - hitPointsOfDamage); if(CurrentHitPoints < 0) { CurrentHitPoints = 0; } } } }
TestPlayer.cs
using System; using System.Runtime.Remoting; using Engine; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TestEngine { [TestClass] public class TestPlayer { [TestMethod] public void TestHitPointsAreSubtracted() { Player player = new Player(10); player.ReceiveDamage(3); Assert.AreEqual(7, player.CurrentHitPoints); } [TestMethod] public void TestNegativeDamageDoesNotChangeCurrentHitPoints() { Player player = new Player(10); player.ReceiveDamage(-3); Assert.AreEqual(10, player.CurrentHitPoints); } [TestMethod] public void TestMoreDamageThanCurrentHitPointsEqualsZero() { Player player = new Player(10); player.ReceiveDamage(13); Assert.AreEqual(0, player.CurrentHitPoints); } } }