a public property called CurrentStatus, which returns the status of the current subscription as an
enumeration value:
Vb
Public Class Subscription
Public Enum Status
Temporary
Financial
Unfinancial
Suspended
End Enum
Public Property PaidUpTo As Nullable(Of Date)
Public ReadOnly Property CurrentStatus As Status
Get
If Not Me.PaidUpTo.HasValue Then Return Status.Temporary
If Me.PaidUpTo > Now Then
Return Status.Financial
Else
If Me.PaidUpTo >= Now.AddMonths(-3) Then
Return Status.Unfinancial
Else
Return Status.Suspended
End If
End If
End Get
End Property
End Class
Code snippet Subscriptions\Subscription.vb
c#
public class Subscription
{
public enum Status
{
Temporary,
Financial,
Unfinancial,
Suspended
}
public DateTime? PaidUpTo { get; set; }
public Status CurrentStatus
Your first Test Case .
193
{
get
{
if (this.PaidUpTo.HasValue == false)
return Status.Temporary;
if (this.PaidUpTo > DateTime.Today)
return Status.Financial;
else
{
if (this.PaidUpTo >= DateTime.Today.AddMonths(-3))
return Status.Unfinancial;
else
return Status.Suspended;
}
}
}
}
Code snippet Subscriptions\Subscription.cs
As you can see from the code snippet, four code paths need to be tested for the Current
Status property. To test this property you create a separate SubscriptionTest test class
in a new test project, into which you add a test method that contains the code necessary
to instantiate a Subscription object, set the PaidUpTo property, and check that the
CurrentStatus property contains the correct result. Then you keep adding test methods
until all of the code paths through the CurrentStatus property have been executed
and tested.
Fortunately, Visual Studio automates the process of creating a new test project, creating
the appropriate SubscriptionTest class and writing the code to create the Subscription
object. All you have to do is complete the test method. It also provides a runtime engine that
is used to run the test case, monitor its progress, and report on any outcome from the test.
Therefore, all you have to do is write the code to test the property in question. In fact, Visual
Studio generates a code stub that executes the property being tested. However, it does not
generate code to ensure that the Subscription object is in the correct initial state; this you
must do yourself.
You can create empty test cases from the Test menu by selecting the New Test item. This prompts
you to select the type of test to create, after which a blank test is created in which you need to
manually write the appropriate test cases. However, you can also create a new unit test that contains
much of the stub code by selecting the Create Unit Tests menu item from the right-click context menu
of the main code window. For example, right-clicking within the CurrentStatus property and
selecting this menu item brings up the Create Unit Tests dialog displayed in Figure 11-1. This dialog
shows all the members of all the classes within the current solution and enables you to select the items
for which you want to generate a test stub.
194 .
chaPter 11 uniT TeSTing
fiGure 11-1
If you have a unit test project already in your solution you can generate your new test class into it by
selecting it from the Output Project drop-down list; otherwise, keep the default selection and Visual
Studio will create a new test project for you. Unlike alternative unit test frameworks such as NUnit,
which allow test classes to reside in the same project as the source code, the testing framework
within Visual Studio requires that all test cases reside in a separate test project. When test cases are
created from the dialog shown in Figure 11-1, they are named according to the name of the member
and the name of the class to which they belong.
You can alter this naming convention in the Test Generation Settings dialog,
which you can access by clicking the Settings button. You will find other settings
that allow you to control how the test code is generated as well.
With the CurrentStatus property checked as in Figure 11-1, clicking the OK button generates the
following code (some comments and commented-out code have been removed from this code):
Vb
< TestClass() >
Public Class SubscriptionTest
Private testContextInstance As TestContext
Public Property TestContext() As TestContext
Your first Test Case .
195
Get
Return testContextInstance
End Get
Set(ByVal value As TestContext)
testContextInstance = value
End Set
End Property
<TestMethod()>
Public Sub CurrentStatusTest()
Dim target As Subscription = New Subscription()
‘ TODO: Initialize to an appropriate value
Dim actual As Subscription.Status
actual = target.CurrentStatus
Assert.Inconclusive(“Verify the correctness of this test method.”)
End Sub
End Class
Code snippet SubscriptionTests\SubscriptionTest.vb
c#
[TestClass()]
public class SubscriptionTest
{
private TestContext testContextInstance;
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
[TestMethod()]
public void CurrentStatusTest()
{
Subscription target = new Subscription();
// TODO: Initialize to an appropriate value
Subscription.Status actual;
actual = target.CurrentStatus;
Assert.Inconclusive(“Verify the correctness of this test method.”);
}
}
Code snippet SubscriptionTests\SubscriptionTest.cs
196 .
chaPter 11 uniT TeSTing
The test case generated for the CurrentStatus property appears in the final method of this code
snippet. (The top half of this class is discussed later in this chapter.) As you can see, the test case
was created with a name that reflects the property it is testing (in this case CurrentStatusTest) in
a class that reflects the class in which the property appears (in this case SubscriptionTest). One of
the difficulties with test cases is that they can quickly become unmanageable. This simple naming
convention ensures that test cases can easily be found and identified.
If you look at the test case in more detail, you
can see that the generated code stub contains
the code required to initialize everything for
the test. A Subscription object is created,
and a test variable called actual is assigned
the CurrentStatus property of that object.
All that is missing is the code to actually test
that this value is correct. Before going any
further, run this test case to see what happens
by opening the Test View window, shown in
Figure 11-2, from the Test Windows menu.
Selecting the CurrentStatusTest item and clicking the Run Selection button, the first on the left,
invokes the test. This also opens the Test Results window, which initially shows the test as being
either Pending or In Progress.
fiGure 11-2
The Test View is just one way to select and run a test case. If you right-click the
test case itself in the code window there is a Run Tests option. There is also a
Tests toolbar with an option to run all the tests in the current context. This will
run an individual test case, a whole test class, a test assembly, or all tests in the
solution depending on the current selection. Finally, you can create lists of tests
using the Test List Editor, which you see later in this chapter.
In addition to each of these methods you can also set breakpoints in your code
and run test cases in the debugger by selecting one of the Debug Tests options
from the main toolbar or the Test View window.
Once the test has completed, the Test Results window will look like the one shown in Figure 11-3.
fiGure 11-3
Your first Test Case .
197
You can see from Figure 11-3 that the test case has returned an inconclusive result. Essentially, this
indicates either that a test is not complete or that the results should not be relied upon, because
changes may have been made that would make this test invalid.
You can get more information on the result of any particular test result by double-clicking it.
Figure 11-4 shows the result of double-clicking the inconclusive result for the example. The results
show basic information about the test, the result, and other useful environmental information such
as the computer name, test execution duration, and start and end times.
fiGure 11-4
As well as information about a particular result, you can also get information
about a complete test run by clicking the Run Details button in the Test Results
window. By default, the Test Results window shows the details of the most
recent test run, but Visual Studio stores all of the results from a number of recent
test runs and you can use the Test Run drop-down list to browse the results of
previous runs.
When test cases are generated by Visual Studio, they are all initially marked as inconclusive by
means of the Assert.Inconclusive statement. In addition, depending on the test stub that was
created, there may be additional TODO statements that will prompt you to complete the test case.
Returning to the code snippet generated for the CurrentStatusTest method, you can see both
an Assert.Inconclusive statement and a TODO item. To complete this test case, remove the TODO
comment and replace the Assert.Inconclusive statement with Assert.AreEqual, as shown in the
following code:
Vb
< TestMethod() >
Public Sub CurrentStatusTest()
Dim target As Subscription = New Subscription
Dim actual As Subscription.Status
actual = target.CurrentStatus
Assert.AreEqual(Subscription.Status.Temporary, actual, _
“ Subscription.CurrentStatus was not set correctly. ” )
End Sub
Code snippet SubscriptionTests\SubscriptionTest.vb
198 .
chaPter 11 uniT TeSTing
c#
[TestMethod()]
public void CurrentStatusTest()
{
Subscription target = new Subscription();
Subscription.Status actual;
actual = target.CurrentStatus;
Assert.AreEqual(Subscription.Status.Temporary, actual,
“ Subscription.CurrentStatus was not set correctly. ” );
}
Code snippet SubscriptionTests\SubscriptionTest.cs
Each test shown in the Test Results window has a checkbox next to it allowing it to be selected.
When test results are selected, clicking the Run Tests button in the Test Results window causes only
those selected tests to be run. By default, after a test run any tests that did not pass are selected.
After you fix the code that caused these tests to fail, click the Run Tests button to re-run these test
cases and produce a successful result, as shown in Figure 11-5.
fiGure 11-5
Any test case that makes no assertions is considered to pass, which is why Visual
Studio automatically puts an Assert.Inconclusive warning into generated
test cases. By removing this assertion you are indicating that the test case is
complete. In this example, we have only exercised one code path and you should
add further test cases that fully exercise the other three.
When you first created the unit test at the start of this chapter you may have noticed that, in
addition to the new test project, two items were added under a new solution folder called Solution
Items. These are Chapter11.vsmdi and Local.testsettings.
The .vsmdi file is a metadata file that contains information about the tests within the solution.
When you double-click this file in Visual Studio, it opens the Test List Editor, which is discussed at
the end of this chapter.
The .testsettings file is a Test Run Configuration file. This is an XML file that stores settings
that control how a set of tests, called a test run, is executed. You can create and save multiple run
Your first Test Case .
199
configurations that represent different scenarios, and then make a specific run configuration active
using the Test . Select Active Test Run Configuration menu item. This defines which of the test run
configurations should be used when tests are run.
If you are using the Ultimate edition of Visual Studio then you might also have
a TraceAndTestImpact.testsettings file when you create a new test project.
This Test Run Configuration is used by Visual Studio to implement the Test
Impact Analysis feature which is covered in Chapter 56.
When you double-click to open the Local.testsettings file, it launches a special-purpose editor.
Within this editor you can configure a test run to copy required support files to a deployment
directory, or link to custom startup and cleanup scripts. The editor also includes a Test Timeouts
section, shown in Figure 11-6, which enables you to define a timeout after which a test will be
aborted or marked as failed. This is useful if a global performance limit has been specified for your
application (for example, if all screens must return within five seconds).
fiGure 11-6
Most of these settings can be overridden on a per-method basis by means of test attributes, which
are discussed in the next section.
200 .
chaPter 11 uniT TeSTing
identifying tests using attributes
Before going any further with this scenario, take a step back and consider how testing is carried
out within Visual Studio 2010. As mentioned earlier, all test cases have to exist within test classes
that themselves reside in a test project. But what really distinguishes a method, class, or project as
containing test cases? Starting with the test project, if you look at the underlying XML project file,
you will see that there is virtually no difference between a test project file and a normal class library
project file. In fact, the only difference appears to be the project type: When this project is built
it simply outputs a standard .NET class library assembly. The key difference is that Visual Studio