BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Using Coding Katas, BDD and VS2010 Project Templates: Part 3

Using Coding Katas, BDD and VS2010 Project Templates: Part 3

This is the third and final part of the late Jamie Phillip’s exploration into the world of coding kata’s and Behavior Driven Design. Part one introduced coding katas, part two combined them with behavior driven design.

I’m good, but couldn’t I be better?

Part 3: Visual Studio 2010 Project Template Wizard.

I had started down the path of enlightenment through Katas and BDD and I was beginning to feel all warm and fuzzy, until I realized that if this was the way that I wanted to write my Unit Tests and do my development in the future, it was going to be pretty painful pulling around Eric Lee’s ContextSpecification each and every time. It would be a lot easier if I could just select a BDD Unit Test Project and have all I needed for the project already available after the project was created. After a bit of searching I found various references to the new features of the Project Template Export Wizard and that seemed the most logical solution for me.

In order to be able to work through this example you will need to download and install the Export Template Wizard from the Visual Studio Gallery (do a search on the Gallery site for Export Template Wizard). This is a free Visual Studio Extension from Microsoft that permits the export of an existing Code Project as a Project Template.

Before we dive in to creating our first template, it is important to take a look at some existing templates to get an idea of what we may need.

When you install Visual Studio the templates are located in:

  • \VisualStudioInstallationDirectory\Common7\IDE\ItemTemplates\Language\Locale\
  • \VisualStudioInstallationDirectory\Common7\IDE\ProjectTemplates\Language\Locale\

For example, the following directory contains the Visual Studio project templates for English:

  • C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\1033\

In addition, when you install a template (typically through double-clicking a .vsix file – Microsoft Visual Studio Extension file) it will get placed in the following folder:

  • \User Documents and Settings Directory\Local Settings\Application Data\Microsoft\VisualStudio\10.0\Extensions
Template Tip:

Using Regedit and browsing to the following key, you will see all of the installed extensions for Visual Studio 2010:

HKCU\Software\Microsoft\VisualStudio\10.0\ExtensionManager\EnabledExtensions

The entries seen here are updated automatically by Visual Studio when it starts. So removing one of the Extensions (i.e. deleting the folder specific to the extension) will be reflected in the registry the next time that Visual Studio is started.

For any given template you will see that the content is stored in a ZIP file, which helps to “keep everything together” organizationally. When you inspect the content of one of these ZIP files, you will notice that, at a minimum, they contain a .vstemplate file; which can be thought of as the manifest of the template.

For our purposes, we are interested in the content of the BasicUnitTest Template found at:

C:\Program Files\Microsoft Visual Studio 10.0\Common7

  • \IDE\ItemTemplates\CSharp\1033\BasicUnitTest.zip

Taking a look at an existing template from the VS 2010 installation, we notice that there are specific key words that are used within the code files (AssemblyInfo.cs, etc). In the following code sample, the highlighted text illustrates the various template parameter keywords:

using System;
using System.Text;
using System.Collections.Generic;
$if$ ($targetframeworkversion$ == 3.5)using System.Linq;$endif$
$if$ ($targetframeworkversion$ == 4.0)using System.Linq;$endif$
using Microsoft.VisualStudio.TestTools.UnitTesting;
  
namespace  $rootnamespace$
{
           [TestClass]
           public class $safeitemname$
           {
                     [TestMethod]
                     public void TestMethod1()
                     {
                     }
           }
}

The keywords $rootnamespace$ and $safeitemname$ are reserved template parameters along with $safeprojectname$:

Parameter

Description

$rootnamespace$

The root namespace of the current project. This parameter is used to replace the namespace only in an item being added to a project.

$safeprojectname$

The name provided by the user in the New Project dialog box, with all unsafe characters and spaces removed.

$safeitemname$

The name provided by the user in the Add New Item dialog box, with all unsafe characters and spaces removed.

Inspecting the associated .vstemplate file we can see that the reference to the code file is in the ProjectItem element and has the ReplaceParameters attribute set to true:

  <TemplateContent>
     ...
     <ProjectItem ReplaceParameters="true">UnitTest.cs</ProjectItem>
   </TemplateContent>

This will instruct the Template Wizard to effectively search and replace all of the parameters in the file specified; which in the example above would be UnitTest.cs. Custom Parameters can also be used by simply adding a CustomParameters element to the .vstemplate file:

<TemplateContent>
    ...
    <CustomParameters>
      <CustomParameter Name="$TemplateParameter1$" Value="SomeValue"/>
        <CustomParameter Name="$TemplateParameter2$" Value="SomeOtherValue"/>
      </CustomParameters>
  </TemplateContent>

With this knowledge in mind we will start to put the pieces together to create our BDD Unit Test Template.

  1. Create the type of project that we want to use as the base for our new project template, so for our purposes the best option would be a Unit Test Project:

     

    Once this is created we will have the base from which we shall build the new template (this, of course, can be applied to any project type, not just Unit Tests).
  2. Bear in mind that everything that is in the project will be pulled in to make the new project template, so now we pull in any other code files that we want (in my case it was the ContextSpecification.cs code file from Eric Lee):

     

  3. Add references to assemblies that will require as part of your Project Template, in my case I want to use the MSpec Assembly:

     

    Template Tip:
    It is important to note that you will need to have the same assemblies installed on each machine that the template is to be deployed to; therefore you should restrict the references to what is available as part of the .NET framework of your choice or what is in the GAC. This may mean that as part of your template you will have to create an installer that installs specific assemblies in the GAC – or make it a requirement that the intended user installs the necessary assemblies themselves.
  4. We will need to replace the namespace name that appears in the code file with the appropriate Template Parameter we referred to earlier ($safeprojectname$). I also put the “versions guards” around the using System.Linq declaration:
    using System;
    using System.Text;
    using System.Collections.Generic;
    $if$ ($targetframeworkversion$ == 3.5)using System.Linq;$endif$$if$ ($targetframeworkversion$ == 4.0)
    using System.Linq;$endif$
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    namespace $safeprojectname$
    {
       ...
    }
  5. Seeing as we are going to use the ContextSpecification approach in our test classes, we need to add a using declaration and add the base class definition using the appropriate Template Parameter (in this case I concatenated the word Context so that we can differentiate it from the derived test class):
    using System;
    using System.Text;
    using System.Collections.Generic;
    $if$ ($targetframeworkversion$ == 3.5)using System.Linq;$endif$$if$ ($targetframeworkversion$ == 4.0)
    using System.Linq;
    $endif$
    using Microsoft.VisualStudio.TestTools;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace $safeprojectname$
    {
        public class $safeitemname$Context : ContextSpecification
        {
            /// <summary>
            /// The "Given some initial context" method
            /// </summary>
            protected override void Context()
            {
                // setup your class under test
            }
        }
        ...
    }
    
  6. The next stage is to redefine the Test Class that is in the code file so that it derives from the Context base class we just added. The code that I have used here, I pulled from the Bowling Kata solution described earlier in the article and used the appropriate Template Parameters in place of the Class and Method names:
        /// <summary>
        /// Summary description for $safeitemname$
        /// </summary>
        [TestClass]
        public class $safeitemname$ : $safeitemname$Context
        {
            /// <summary>
            /// The "When an event occurs" method
            /// </summary>
            protected override void BecauseOf()
    { // // TODO: Add behavior setup (Action) here // } /// <summary> /// The "then ensure some outcome" method. /// </summary> [TestMethod] public void TestMethod1() { // // TODO: Add test logic here // } }
  7. With the AssemblyInfo.cs file we will need to replace the references specific to the project we created (i.e. the name of the assembly etc.), this time however we can copy an existing AssemblyInfo from one of the existing template “manifests” (I used "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ProjectTemplates\CSharp\Test\1033\TestProject.zip") on top of our Project AssemblyInfo.cs
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    // General Information about an assembly is controlled through the following 
    // set of attributes. Change these attribute values to modify the information
    // associated with an assembly.
    [assembly: AssemblyTitle("$projectname$")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("$registeredorganization$")] [assembly: AssemblyProduct("$projectname$")] [assembly: AssemblyCopyright("Copyright © $registeredorganization$ $year$")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")]  // Setting ComVisible to false makes the types in this assembly not visible
    // to COM components.  If you need to access a type in this assembly from
    // COM, set the ComVisible attribute to true on that type.
    [assembly: ComVisible(false)]  // The following GUID is for the ID of the typelib if this project is exposed to COM
    [assembly: Guid("$guid1$")]  // Version information for an assembly consists of the following four values:
    //
    //      Major Version
    //      Minor Version
    //      Build Number
    //      Revision
    //
    // You can specify all the values or you can default the Build and Revision Numbers
    // by using the '*' as shown below:
    [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
  8. Now we are ready to run the Template Export Wizard; from the File menu, select File->Export Template as VSIX...:

     

  9. This will open the Export Template as VSIX Wizard dialog, where we can choose either to create a Project Template (from the projects in a solution) or an Item Template (from one of the files in a selected project of the solution) – for our purposes we will choose the only project in the solution for a Project Template. Click Next >:

     

  10. The next screen in the Wizard provides the opportunity to setup how the template is going to be displayed in the “New Project” dialog of Visual Studio. Fill out the fields appropriately and click Next >:

     

    Template Tip:
    If the two fields “Icon Image” and “Preview Image” are not filled in, the Wizard will auto-generate images for you; which may not be to your liking (it looks like a snapshot of a code window – up close and small). It is advisable to create your own images and reference those in the two fields.
    In the example we are working through I prepared my own images to the following dimensions (these are the same dimensions as the wizard generated images):

  11. In the next screen we finalize the options by filling out the “production details”. It is extremely important to note the output location and the two check boxes at the bottom of the dialog.
    The output location is where the VSIX file will be located when you click the Finish button.
    The “Automatically import the template into Visual Studio” check box will indicate to the wizard that once it is complete it should install the template – depending on how you want to work with creating your templates, you may or may not want to have this checked. For the time being I will leave it checked because I want to see the result straightaway.
    The “Display an explorer window on the output files folder” check box is essential if you do not take note of where the output location is – I, personally, like to leave this checked. Fill out the fields appropriately and click Finish:

     

  12. Once the process has finished, open the New Project dialog (either via the File->New->Project... menu option or press CTRL + Shift + N) and .select the Visual C# project types where you will find the newly created Bdd Unit Test project:

     

  13. Create a new project of the BDD Unit Test type and open the UnitTest1.cs file. You will notice that the Template Parameter derived names for the classes (unittest1Context and unittest1) are lower case; which in my mind is not ideal:
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    namespace BDDUnitTest1
    {
         public class unittest1Context : ContextSpecification
         {          ...      }        /// <summary>      /// Summary description for unittest1
         /// </summary>      [TestClass]      public class unittest1 : unittest1Context      {          ...      } }
  14. Reviewing what was stated about the format of the Visual Studio Extension file (.vsix); we can rename the file produced by the Project Template Export Wizard to a .ZIP extension:

     

  15. Opening the ZIP file we find the following structure; the internal zip file (in the example below – BddUnitTest.zip) is the file we are interested in, so unzip the renamed zip file to an appropriate location:

     

  16. With all of the ZIP files appropriately unzipped, open the .vstemplate file using the “Open With...” feature of Visual Studio (choose XML (Text) Editor when prompted for the type of editor):

     

  17. With the .vstemplate file open in the XML Editor of Visual Studio, you will notice that the TargetFileName attribute of the ProjectItem element for each file contains lowercase text, despite the fact that the actual file names are in Camel-Case:
    <?xml version="1.0"?>
    <VSTemplate xmlns:xsi="http://www.w3.org/2001/XMLSchema-
    instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"Type="Project" Version="3.0.0" xmlns="http://sc hemas.microsoft.com/developer/vstemplate/2005">
       <TemplateData>
         <Name>BDD Unit Test</Name>
         <Description>A Test project based on Behavior Driven Development (BDD) principles</Description>
         <Icon>__Template_small.png</Icon>
         <PreviewImage>__Template_large.png</PreviewImage>
         <ProjectType>CSharp</ProjectType>
         <ProjectSubType />
         <SortOrder>1000</SortOrder>
         <DefaultName>BDDUnitTest</DefaultName>
         <ProvideDefaultName>true</ProvideDefaultName>
         <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
         <LocationField>Enabled</LocationField>
       </TemplateData>
       <TemplateContent>
         <Project File="BddUnitTest.csproj" TargetFileName="BddUnitTest.csproj" ReplaceParameters="true">
           <ProjectItem
             TargetFileName="contextspecification.cs"
             ReplaceParameters="true">contextspecification.cs</ProjectItem>
           <ProjectItem
             TargetFileName="assemblyinfo.cs"
             ReplaceParameters="true">properties\assemblyinfo.cs</ProjectItem>
           <ProjectItem
             TargetFileName="unittest1.cs"
             ReplaceParameters="true">unittest1.cs</ProjectItem>
         </Project>
         <CustomParameters />
       </TemplateContent>
    </VSTemplate>
  18. Simply update the TargetFileName attribute of each ProjectItem element to use Camel case exactly as the actual filenames (it is not necessary to update the Text part of the ProjectItem element as this does not influence how the Wizard generates the file when creating the project):
    <?xml version="1.0"?>
    <VSTemplate xmlns:xsi="http://www.w3.org/2001/XMLSchema-
    instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Type="Project" Version="3.0.0" xmlns="http://sc hemas.microsoft.com/developer/vstemplate/2005">
      <TemplateData>
    ...
    </TemplateData>
    <TemplateContent>
         <Project File="BddUnitTest.csproj" TargetFileName="BddUnitTest.csproj" ReplaceParameters="true">
           <ProjectItem
             TargetFileName="ContextSpecification.cs"
             ReplaceParameters="true">contextspecification.cs</ProjectItem>
           <ProjectItem
             TargetFileName="AssemblyInfo.cs"
             ReplaceParameters="true">properties\assemblyinfo.cs</ProjectItem>
           <ProjectItem
             TargetFileName="UnitTest1.cs"
             ReplaceParameters="true">unittest1.cs</ProjectItem>
         </Project>
         <CustomParameters />
       </TemplateContent>
    </VSTemplate>
  19. The next step is to re-ZIP the files in the order that they were unzipped, so that the internal ZIP file that contains the updated .vstemplate file (in this case BddUnitTest.zip) is created first and put in its own folder (with the same name as the ZIP file):

     

  20. Next we ZIP the remaining files into the final ZIP that will become the “updated” Visual Studio Extension file (.vsix):

     

  21. Rename the newly created ZIP file to the original name of the vsix that was created when we first performed the Export (in this case it was BDD Unit Test.vsix):

     

  22. Close all instances of Visual Studio 2010 and navigate to the folder \User Documents and Settings Directory\Local Settings\Application Data\Microsoft\VisualStudio\10.0\Extensions and delete the folder related to the extension that was created in the original export (in this case it is \User Documents and Settings Directory\Local Settings\Application Data\Microsoft\VisualStudio\10.0\Extensions\JasPWarE\BDD Unit Test):

     

  23. In Windows explorer, navigate to the newly created vsix file (in this case it is BDD Unit Test.vsix) and double click it; this will open the Visual Studio Extension Installer:

     

  24. Click Install and the installation process will execute and install the updated template:

     

  25. Once the Extension is installed we can verify that the update has worked by starting up Visual Studio 2010 and creating a new BDD Unit Test project and checking the code file:
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;  
    using Microsoft.VisualStudio.TestTools;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
      
    namespace BDDUnitTest1
    {
         public class UnitTest1Context : ContextSpecification
         {
             ...
         }
      
         /// <summary>
         /// Summary description for UnitTest1
         /// </summary>      [TestClass]      public class UnitTest1 : UnitTest1Context      {          ...      } }

Although there was a certain amount of manual intervention at the end to perform clean-up, it helped expose how easy it is to update existing .vsix files and change a template. With the new template available for use, creating BDD Unit Tests will be easier as there is no worry about referencing the correct assemblies or including the appropriate code files; all you have to concentrate on is writing the actual tests themselves.

The journey from coding Kata to BDD and then Project Template has shown us an assortment of practical exercises that we can use to improve on various levels; through Katas we improve our coding techniques; through BDD we improve the way that we approach our designs and write our Unit Tests; through Project Templates we can improve the process of creating code projects.

Rate this Article

Adoption
Style

BT