Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Visual Studio 2005 (2006) [eng]

.pdf
Скачиваний:
132
Добавлен:
16.08.2013
Размер:
21.9 Mб
Скачать

Chapter 53

Figure 53-9

Advanced

Up until now, you have seen how to write and execute unit tests. This section goes on to examine how you can add custom properties to a test case and how you can use the same framework to test private methods and properties.

Custom Properties

The testing framework provides a number of test attributes that can be applied to a method to record additional information about a test case. This information can be edited via the Properties window and updates the appropriate attributes on the test method. There are times when you want to drive your test methods by specifying your own properties, which can also be set using the Properties window. To do this, add TestProperty attributes to the test method. For example, the following code adds two attributes to the test method to enable you to specify an arbitrary date and an expected status. This might be convenient for ad hoc testing using the Test View and properties window:

<TestMethod()> _ <TestProperty(“SpecialDate”, “1/1/2005”)> _

<TestProperty(“SpecialStatus”, “Suspended”)> _ Public Sub SpecialCurrentStatusTest()

Dim target As Subscription = New Subscription

target.PaidUpTo = CDate(Me.TestContext.Properties.Item(“SpecialDate”)) Dim val As Subscription.Status =

CType([Enum].Parse(GetType(Subscription.Status), _

756

Unit Testing

CStr(Me.TestContext.Properties.Item(“SpecialStatus”))),

Subscription.Status)

Assert.AreEqual(val, target.CurrentStatus, _

“Correct status not set for Paid up date {0}”, target.PaidUpTo)

End Sub

Using the Test View to navigate to this test case and accessing the Properties window, you can see that this generates two additional properties: SpecialDate and SpecialStatus, as shown in Figure 53-10.

Figure 53-10

The Properties window can be used to adjust the SpecialDate and SpecialStatus values. Unfortunately, the limitation here is that there is no way to specify the data type for the values. As such, the property grid only displays and allows edits as if they were String data types.

Note one other limitation to using custom properties as defined for the SpecialCurrentStatusTest method. Looking at the code, you can see that you are able to access the property values using the Properties dictionary provided by the TestContext. Unfortunately, although custom properties automatically appear in the Properties window, they are not automatically added to this Properties dictionary. As such, a bit of heavy lifting has to be done to extract these properties from the custom attributes list and place them into the Properties dictionary. Luckily, this can be done in the TestInitialize method, as illustrated in the following code. Note that although this method will be executed for each test case in the class, and as such will load all custom properties, it is not bound to any particular test case, as it uses the TestContext.Name property to look up the test method being executed:

<TestInitialize()> _ Public Sub Setup()

Dim t As Type = Me.GetType

Dim mi As Reflection.MethodInfo = t.GetMethod(Me.TestContext.TestName) Dim MyType As Type = GetType(TestPropertyAttribute)

Dim attributes As Object() = mi.GetCustomAttributes(MyType, False)

For Each attrib As TestPropertyAttribute In attributes

Me.TestContext.Properties.Add(attrib.Name, attrib.Value)

Next

End Sub

757

Chapter 53

Testing Private Members

One of the selling features of unit testing is that it is particularly effective for testing the internals of your class to ensure that they function correctly. The assumption here is that if each of your classes works in isolation, then there is a better chance that they will work together correctly; and in fact, unit testing can be used to test classes working together. However, you might be wondering how well the unit testing framework handles testing private methods.

One of the features of the .NET Framework is the capability to reflect over any type that has been loaded into memory and execute any member regardless of its accessibility. This functionality does come at a performance cost, however, as the reflection calls obviously include an additional level of redirection, which can prove costly if done frequently. Nonetheless, for testing, reflection enables you to call into the inner workings of a class and not worry about the potential performance penalties for making

those calls.

The other more significant issue with using reflection to access non-public members of a class is that the code to do so is somewhat messy. Luckily, Visual Studio 2005 does a very good job of generating a

wrapper class that makes testing even private methods easy. To show this, return to the CurrentStatus property and change its access from public to private. If you follow the same process you did previously to generate the test method for this property, you will see that an additional class is generated

to support the test method. The first snippet of the following code is the new test method that is generated (there may be naming differences if your Subscription class is in a different namespace from

DeveloperNews):

<TestMethod()> _

Public Sub PrivateCurrentStatusTest()

Dim target As Subscription = New Subscription

Dim val As Subscription.Status = Subscription.Status.Temporary

Dim accessor As DeveloperNews_SubscriptionAccessor = _

New DeveloperNews_SubscriptionAccessor(target)

Assert.AreEqual(val, accessor.CurrentStatus, _ “DeveloperNews.Subscription.CurrentStatus was not set correctly.”)

End Sub

As you can see, the preceding example uses an instance of the DeveloperNews_SubscriptionAccessor class to access the CurrentStatus property. In the test project is a new file, VSCodeGenAccessors.vb, which contains this class definition:

<System.Diagnostics.DebuggerStepThrough(), _ System.CodeDom.Compiler.GeneratedCodeAttribute _

(“Microsoft.VisualStudio.TestTools.UnitTestGeneration”, “1.0.0.0”)> _ Friend Class DeveloperNews_SubscriptionAccessor

Inherits BaseAccessor

Protected Shared m_privateType As PrivateType = _

758

Unit Testing

New PrivateType(GetType(Global.DeveloperNews.Subscription))

Friend Sub New(ByVal target As Global.DeveloperNews.Subscription)

MyBase.New(target, m_privateType)

End Sub

Friend Property m_Description() As String Get

Dim ret As String = _ CType(m_privateObject.GetFieldOrProperty(“m_Description”),String)

Return ret End Get

Set

m_privateObject.SetFieldOrProperty(“m_Description”, value) End Set

End Property

Friend Property m_PaidUpTo() As System.Nullable(Of Date) Get

Dim ret As System.Nullable(Of Date) = _ CType(m_privateObject.GetFieldOrProperty(“m_PaidUpTo”), _

System.Nullable(Of Date))

Return ret End Get

Set

m_privateObject.SetFieldOrProperty(“m_PaidUpTo”, value) End Set

End Property

Friend Property SubscriptionOpenedOn() As Date Get

Dim ret As Date = _ CType(m_privateObject.GetFieldOrProperty(“SubscriptionOpenedOn”),Date) Return ret

End Get Set

m_privateObject.SetFieldOrProperty(“SubscriptionOpenedOn”, value) End Set

End Property

Friend ReadOnly Property CurrentStatus() As Subscription.Status

Get

Dim ret As Subscription.Status = _

CType(m_privateObject.GetProperty(“CurrentStatus”), Subscription.Status)

Return ret

End Get

End Property

End Class

It’s clear from this code snippet that another base class is used to provide the underlying reflection methods. The DeveloperNews_SubscriptionAccessor class inherits from the BaseAccessor object to provide access to specific members, instead of having to use a string-based lookup, which would make the test code very untidy.

759

Chapter 53

Summar y

This chapter described how unit testing can be used to ensure the correct functionality of your code using proper testing. The unit testing framework within Visual Studio Team System is quite comprehensive, enabling you to both document and manage test cases.

The testing framework can be fully exercised using an appropriate Data Source to minimize the repetitive code that has to be written to test your code. It can also be extended to test all the inner workings of your application.

Chapter 56 provides more detail on the various test windows available to testers, and the other types of testing you can perform using Visual Studio Team System.

760

Part X

Extensions for Visual Studio 2005

Chapter 54: InfoPath 2003 Toolkit

Chapter 55: Visual Studio Tools for Office

Chapter 56: Visual Studio Team System

InfoPath 2003 Toolkit

When Microsoft first released InfoPath, it was seen as an excellent option for enabling corporations to build forms easily without having to resort to writing code. While it was indeed the answer for many, providing the capability to create complex form designs with a robust graphical user interface and easy connection to data sources, all bound up in XML, it was problematic when a developer wanted to create a form that was backed by code.

One of the reasons for this is that the code behind an InfoPath form is written in JavaScript rather than a full programming language, and it is interpretive instead of being a properly compiled application. As a result, form design with code was more suited to web designers, given their familiarity with JavaScript and XML formatting, than application developers.

With the release of Visual Studio 2005, Microsoft also released the InfoPath 2003 Toolkit for Visual Studio 2005. The Toolkit integrates with InfoPath 2003 SP1 to provide a managed code solution for creating and managing InfoPath forms that rely on program code to perform part of their functionality. This chapter discusses some of the new features in the object model of InfoPath 2003 with the release of SP1, and how they can be harnessed with the Toolkit for Visual Studio 2005.

Creating Managed InfoPath Solutions

When you install the Toolkit, it merges a number of files into the Visual Studio 2005 environment. The first addition to notice is the additional project templates created for Visual Basic and C# languages. These are added to the Office group (see Figure 54-1).

If the Office groups don’t already exist (for example, if you haven’t installed Visual Studio Tools for Office 2005), then the installation of the InfoPath 2003 Toolkit creates them for you.

Chapter 54

When you create an InfoPath Form Template project, you are given the option to create an associated solution as shown in Figure 54-1. This option is not available if you are adding the project to an existing solution.

Figure 54-1

After specifying the name of your InfoPath-based project and clicking OK to tell Visual Studio to create the project, the Microsoft Office Project Wizard opens with a single page of options (see Figure 54-2).

Figure 54-2

764

InfoPath 2003 Toolkit

If you’re creating the project from scratch, you should use the Create New Form Template option. Unfortunately, the wizard does not allow you to name the InfoPath form, so it inherits the name from the project name.

When using an existing form template, it doesn’t have to have the same name. When you select an existing template, the wizard makes a copy of it in the project folder along with any associated files. To confirm that the action has taken place, Visual Studio displays a dialog explaining what happened.

When you convert an existing form template that contains code, the code is saved as associated JavaScript (.js) files in the project folder and added to the Solution Explorer. These files may contain code supporting one or more objects. Therefore, be careful when you’re creating solutions that use older templates.

For example, if a template had a JavaScript OnClick event for a button on the form design, then when you wrap the InfoPath with the new .NET-based project the JavaScript function will still be present; but when you click on the Edit Form Code from InfoPath, the wizard will add the new managed code OnClick event handler in your Visual Basic or C# code.

The Generated Solution

An InfoPath project is slightly more complex than a typical Windows Forms application because the different components of the InfoPath template are separated into individual files. Figure 54-3 shows how a basic InfoPath project first appears.

Figure 54-3

The code associated with the form is split into an AssemblyInfo.vb or AssemblyInfo.cs file that can be used to sign the assembly (which you’ll need to do if you want to deploy your form outside the semi-trust security zone of InfoPath), and a FormCode module that is used to perform the initialization and cleanup code for the InfoPath form. This module also connects your .NET project to the InfoPath namespace.

The _Startup and _Shutdown functions should be present in every InfoPath managed solution in order for it to work correctly. In addition, the project template automatically generates managed code objects for the InfoPath application and form objects for use in your own code. The following listing shows the

765