Testing in Visual Studio Primer
Visual studio offers a variety of tools to perform tests against your application. The tests available range from unit tests to load tests. This article is a primer for using the unit test framework in Visual Studio.
Strictly speaking, a unit test aims to test a single unit of a code base. Typically, this unit corresponds one-to-one with a class. However, there is nothing in the Visual Studio unit test framework that prevents a developer from testing more than a single ‘unit’. For example, various types of integration tests can also be scripted within the unit test framework. MSDN has frameworks and recommendations for performing various kinds of tests. For a MSDN guided approach, see “Creating System Tests Using Visual Studio”.
This article will focus on simply setting up tests within the VS unit test framework. The actual flavor of testing performed within the framework is up to the reader.
Visual Studio provides a Unit Test project for creating and managing unit tests. The project type can be selected when creating a new project:
A unit test class is created within the test project by default:
Using this default class is a good way to become familiar with the basics of the framework.
A unit test class is simply a normal class with a few annotations. The [TestClass] annotation flags the complier to (among other things) look for tests within this class. Tests are defined at the method level. A single test corresponds to a method within the test class. Methods intended to be ran as a test are marked with the [TestMethod] annotation. A solution can have any number of test classes each with any number of test methods.
To create a test, simply populate a test method. A common approach to creating a test is to script the intended test steps using the Arrange, Act and Assert (AAA) methodology. AAA defines 3 broad steps to scripting a test.
Arrange: Query resources and services. Initialize objects. Essentially, put everything in place needed to test.
Act: Execute the tests. Coordinate and direct the objects in question to perform the required test.
Assert: Examine the result of the test and verify the application state is as expected. Test will fail if criteria is not met.
As an example, consider the following test:
ILogger logger = newMockLogger(); //Create mock logger
TestObject to = newTestObject(logger); //Instantiate object to be tested, injecting logger dependency
to.RunAction(); //Run the desired actions
Assert.IsTrue(to.StateIsValid()); //Validate application state
This a simple yet standard unit test.
The domain has been created with maintainability and extensibility in mind. Inversion of control has been implemented using dependency injection to encourage maintainability and extensibility. As a side effect, the domain is easily tested with unit tests. Units are easily isolated for testing by injecting mock objects for dependencies.
- In the Arrange step, we create a mock logger object. We then inject the mock logger into the object being tested using constructor injection. (Note: Dependency injection is by no means required to run tests, it is in this example simply to help illustrate the Arrange step and follow common conventions for unit testing).
- We want to test the RunAction() method on TestObject. So, we execute the RunAction() method in the Act step.
- In the Assert step, we examine the tested object and verify it is in a valid state. We perform these verifications using the unit test Assertobject. The Assert object provides methods to evaluate conditions. Here we evaluate the conditions IsTrue, however many other evaluations are available such as IsFalse, IsNotNull, AreEqual, AreSame and so on. If the condition being evaluated returns false, the test fails.
During compilation, all the tests in the solution are accessible through the Test Explorer window:
The tests can be run individually, all at once, or arranged in playlists using the menu at the top of the Test Explorer window.
If a test passes, it will be marked with a green checkbox along with the execution time:
If a test fails, it will be marked with a red ‘X’ along with the execution time:
Many more features are available in the test framework, the above description was provided as a brief overview to quickly become familiar with the test framework in Visual Studio. Even with this simple example one can begin to see the power and utility of the test framework.