Visual Studio will automatically create a stub for a test project, which is where we will start.

Creating a Unit Test Project in Visual Studio

Unit tests are typically placed in a separate project (resulting in a distinct assembly) from your application code. In Visual Studio 2008 or 2012, you can create a unit test project by right-clicking on the solution and selecting Add followed by New Project from the pop-up menu:

Adding a New Project

From the dialog box that appears, select a Test project:

VS2008 New Test ProjectVS2012 New Test Project

Visual Studio 2008 will create a stub file, “UnitTest1.cs” (if you selected the C# language), with a variety of helpful comments in the stub. Visual Studio 2012 creates a much terser stub:

For Visual Studio 2008 Users

Visual Studio 2008 will also create a TestContext class—this no longer exists in VS2012 and we will ignore it—use the previous stub from VS2012 instead.

Also, delete the “ManualTest1.mht” file, otherwise you will be prompted to select test results and enter test notes manually.

Test Fixtures

Notice that the class is decorated with the attribute TestClass. This defines the test fixture—a collection of test methods.

Test Methods

Notice that the method is decorated with the attribute TestMethod. This defines a method, which the test fixture will run.

The Assert Class

The assert class defines the following static methods that can be used to verify a method’s computation:

AreEqual/AreNotEqual

AreSame/AreNotSame

IsTrue/IsFalse

IsNull/IsNotNull

IsInstanceOfType/IsNotInstanceOfType

Many of these assertions are overloaded and it is recommended that you review the full documentation that Microsoft provides.

Fundamentals of Making an Assertion

Note that the following examples use VS2008.

Assertions are the backbone of every test. There are a variety of assertions one can make regarding the results of a test. To begin with, we’ll write a simple assertion that states “one equals one,” in other words, a truism:

AreEqual/AreNotEqual

The AreEqual and AreNotEqual methods compare:

objects

doubles

singles

strings

typed data

They take the form of comparing the expected (the first parameter) with the actual (the second parameter) value. With regard to single and double values, “within a certain accuracy” can be specified. Lastly, all the overloads have the option to display a message (optionally formatted) if the assertion fails.

With regard to object equality, this method compares whether the instances are identical:

The preceding test passes, as object1 and object2 are not equal. However, if the class overrides the Equals method, then the equality is based on the comparison made by the Equals method implemented in the class. For example:

Even though the class AnObject overrides the Equals operator, the preceding test passes because the instances of the two objects are not the same.

IsTrue/IsFalse

These two methods allow you to test the truth of a value comparison. From a readability perspective, the IsTrue and IsFalse methods are typically used for value comparisons, whereas AreEqual and AreSame are typically used to compare instances (objects).

Inconclusive

The Assert.Inconclusive method can be used to specify that either the test or the functionality behind the test has not yet been implemented and therefore the test is inconclusive.

What Happens When An Assertion Fails?

With regard to Visual Studio unit testing, when an assertion fails, the Assert method throws an AssertFailedException. This exception should never be handled by your test code.

Other Assertion Classes

There are two other assertion classes:

CollectionAssert

StringAssert

As their names imply, these assertions operate on collections and strings, respectively.

Collection Assertions

These methods are implemented in the Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert class. Note that the collection parameter in these methods expects the collection to implement ICollection (contrast this with NUnit, which expects IEnumerable).

AllItemsAreInstanceOfType

This assertion verifies that objects in a collection are of the same type, which includes derived types of the expected type, as illustrated here:

AreEqual/AreNotEqual

These tests assert that two collections are equal. The methods include overloads that allow you to provide a comparator method. If an object overrides the Equals method, that method will be used to determine equality. For example:

These two collections are equal because the class AnObject overrides the Equals method (see previous example).

Note that to pass the assertion, the lists must be of the same length and are considered not equal if the lists are identical except in a different order. Compare this with the AreEquivalent assertion described next.

AreEquivalent/AreNotEquivalent

This assertion compares two lists and considers lists of equal items to be equivalent regardless of order. Unfortunately, there appears to be a bug in the implementation, as this test fails:

These attributes precede and follow the execution of all tests within the fixture (class), as well as before and after each test in the fixture.

Note that the methods decorated with this attribute must be static.

ClassInitialize

If a method is decorated with this attribute, the code in the method is executed prior to running all tests in the fixture. Note that this method requires a TestContext parameter.

This method is useful to allocate resources or instantiate classes that all the tests in the fixture rely on. An important consideration with resources and objects created during the fixture initialization is that these resources and objects should be considered read-only. It is not advisable for tests to change the state of resources and objects on which other tests depend. This includes connections to services such as database and web services whose connection might be put into an invalid state as the result of an error in a test, thus invalidating all of the other tests. Furthermore, the order in which tests run is not guaranteed. Altering the state of a resource and object created in the fixture initialization may result in side effects, depending on the order in which tests are run.

ClassCleanup

A method decorated with this attribute is responsible for de-allocating resources, closing connections, etc., that were created during class initialization. This method will always execute after running the tests within the fixture, regardless of the success or failure of the tests themselves.

TestInitialize

Similar to the ClassInitialize attribute, a method decorated with this attribute will be executed for each test prior to running the test. One of the purposes of this attribute is to ensure that resources or objects allocated by the ClassInitialize code are initialized to a known state before running each test.

TestCleanup

Complementing the TestInitialize attribute, methods decorated with TestCleanup will be executed at the completion of each test.

Setup and Teardown Flow

The following code demonstrates the flow of fixture and test setup and teardown with relation to the actual tests:

If you try this, the test engine fails to run any unit tests, reporting:

“UTA013: UnitTestExamplesVS2008.Fixture2: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly.”

Therefore, only one AssemblyInitialize and one AssemblyCleanup method can exist for an assembly, regardless of the number of test fixtures in that assembly. It is therefore recommended that no actual tests are put into the class that defines these methods:

Ignore

Ignore a Test Method

the test will not run. Unfortunately, the Visual Studio Test Result pane does not indicate that there are tests currently ignored:

Ignored Tests Are Not Shown

Compare this with NUnit, which clearly shows ignored tests:

NUnit Shows Ignored Tests

The NUnit display marks the entire test tree as “unknown” when one or more test methods are marked as “Ignore.”

Ignore a Test Fixture

An entire fixture’s methods can be ignored by using the Ignore attribute at the class level:

[TestClass, Ignore]
public class SetupTeardownFlow
{
... etc ...

Clearing the Test Cache

If you add the Ignore attribute to a method, you may notice that Visual Studio still runs the test. It is necessary to clear the test cache for Visual Studio to pick up the change. One way to do this is to clean the solution and rebuild it.

Owner

Used for reporting purposes, this attribute describes the person responsible for the unit test method.

DeploymentItem

If the unit tests are being run in a separate deployment folder, this attribute can be used to specify files that a test class or test method requires in order to run. You can specify files or folders to copy to the deployment folder and optionally specify the target path relative to the deployment folder.

Description

Used for reporting, this attribute provides a description of the test method. Oddly, this attribute is available only on test methods and is not available on test classes.

HostType

For test methods, this attribute is used to specify the host that the unit test will run in.

Priority

This attribute is not used by the test engine, but could be used, via reflection, by your own test code. The usefulness of this attribute is questionable.

WorkItem

If you are using Team Foundation Server (TFS), you can use this attribute on a test method to specify the work item ID assigned by TFS to the specific unit test.

CssIteration/CssProjectStructure

These two attributes are used in relationship with TeamBuild and TestManagementService and allow you to specify a project iteration to which the test method corresponds.

Parameterized Testing with the DataSource Attribute

Microsoft’s unit test engine supports CSV, XML, or database data sources for parameterized testing. This is not exactly true parameterized testing (see how NUnit implements parameterized testing) because the parameters are not passed to the unit test method but must be extracted from the data source and passed to the method under test. However, the ability to load test data into a DataTable from a variety of sources is helpful for driving test automation.

Again, observe that the test method code itself is the same—the only thing we’ve done here is change the DataSource definition.

TestProperty Attribute

The MSDN documentation for this attribute illustrates declaring a TestProperty name-value pair and then, using reflection, acquiring the name and value. This seems to be an obtuse way of creating parameterized tests.

Furthermore, the code, described on Craig Andera’s blog, to use the TestProperty attribute to parameterize the test initialization process does not affect the TestContext.Properties collection on Visual Studio 2008 or Visual Studio 2012.