Deduplicating SQL data

Tim Murphy on Thursday, April 29, 2010 10:59 AM

So you stuff up and didn’t write unit tests to properly test that duplicate data does not get appended to the database. Plus you were a bigger idiot and had not set a unique index on the column.

First thing is to write those unit tests to ensure code is correct.

Now we need to deduplicate the existing data. The following SQL query show you how many affected records there are.

SELECT LoweredSlug, Count(LoweredSlug)
FROM "Issues.Issues"
GROUP BY LoweredSlug
HAVING (COUNT(LoweredSlug) > 1)

Of course you need to change the table and column names accordingly.

A quick and dirty why to the remove the duplicates to add the primary key value to the end of the affected data (ie LoweredSlug-PrimaryKey). Here’s the SQL command:

UPDATE "Issues.Issues"
SET Slug = Slug + "-" + IssueKey, 
    LoweredSlug = LoweredSlug + "-" + IssueKey
WHERE LoweredSlug IN
(
    SELECT LoweredSlug
    FROM "Issues.Issues"
    GROUP BY LoweredSlug
    HAVING (COUNT(LoweredSlug) > 1)
)

No go and fix the database schema to not allow duplicates!

How to install Python and CGI scripts on IIS 7 (Windows 7, Vista, Server 2008)

Tim Murphy on Friday, April 16, 2010 12:00 PM

  1. Download and install required version of Python.
  2. Open Internet Information Services.
  3. Select the site that requires Python CGI scripts.
  4. Ensure Features View is the current view in the middle pane of IIS.
  5. Double-click Handler Mappings.
  6. Click Add Script Map… in Actions pane.
  7. Set the Request path value to *.cgi.
  8. Set the Executable value to C:\Python26\python.exe -u "%s %s" (Change path appropriately)
  9. Set the Name value to PythonCGI.
  10. Click Request Restrictions… button.
  11. Click the Access tab in Request Restrictions dialog.
  12. Select Execute.
  13. Click OK to return to Add Script Map dialog. 
  14. Click OK to save the mapping.
  15. Click Yes to Do you want to enable this ISAPI application? dialog.
  16. Right-click the newly create PythonCGI mapping and select Edit Feature Permissions…
  17. Ensure every permission is ticked. Click OK.
  18. Install a simple CGI script to test the web site.
    print 'Status: 200 OK'
    print 'Content-type: text/html'
    print
     
    print '<html><head>'
    print ''
    print '<h1>It works!</h1>'
    print ''
    print ''
  19. Point your browser to the test script and hey presto it should be working.

References

How to install Python and CGI scripts on Windows Server 2003

Tim Murphy on Sunday, April 11, 2010 1:26 PM

  1. Download and install required version of Python.
  2. Open Internet Information Services.
  3. Open the Properties dialog for web site that required Python CGI scripts.
  4. Go to the Home Directory tab.
  5. Ensure Execute permissions is set to Scripts Only.
  6. Click the Configuration button and select the Mappings tab.
  7. Click Add… button.
  8. Set the Executable value to C:\Python26\python.exe -u "%s %s" (Change path appropriately)
  9. Set the Extension value to .cgi.
  10. Set Verbs to All Verbs.
  11. Tick Script engine on.
  12. Tick Verify that file exists on.
  13. Click OK to return to the Application Configuration dialog.
  14. Click OK to return to the web site Properties dialog.
  15. Click OK to return to Internet Information Services manager.
  16. Install a simple CGI script to test the installation.
    print 'Status: 200 OK'
    print 'Content-type: text/html'
    print
     
    print '<html><head>'
    print ''
    print '<h1>It works!</h1>'
    print ''
    print ''
    
  17. Point your browser to the test script and hey presto it should be working.

References

Building a MVP Framework: Part 3: The About Dialog

Tim Murphy on Sunday, January 10, 2010 8:48 AM

In this post of the series we will build the About Dialog.

The Goal

Display a dialog showing the application name, version, link to blog and OK button.

The Model

I’m quickly learning that there is an effective order to follow when develop a new form for an MVP application:

  1. Model
  2. Presenter
  3. View

It is best to write the model first because both the Presenter and the View rely on the model. In particular the Model should be written and compiled before the view so you can use Data Sources in the designer to drag and drop the Model on to the form.

The AboutViewModel has just three readonly properties: ApplicationName, ApplicationVersion, BlogTitle & BlogURL.

Refactoring

Obviously the AboutViewModel shares data with the AppWindowViewModel. This shows the benefit of creating ViewModels, we can now refactor the common properties into Models and have each ViewModel refer to the model and our Views are unaffected.

Public Class AboutViewModel

    Private mApplication As New ApplicationModel
    Private mBlog As New BlogModel

    Public ReadOnly Property Application() As ApplicationModel
        Get
            Return mApplication
        End Get
    End Property

    Public ReadOnly Property Blog() As BlogModel
        Get
            Return mBlog
        End Get
    End Property

End Class

Public Class AppWindowViewModel

    Public mApplication As New ApplicationModel
    Public mBlog As New BlogModel

    Public ReadOnly Property Name() As String
        Get
            Return mApplication.Name
        End Get
    End Property

    Public ReadOnly Property BlogTitle() As String
        Get
            Return mBlog.Name
        End Get
    End Property

    Public ReadOnly Property BlogURL() As String
        Get
            Return mBlog.URL
        End Get
    End Property

End Class

Namespace Models

    Public Class Application

        Public ReadOnly Property Name() As String
            Get
                Return "Building a MVP Application"
            End Get
        End Property

        Public ReadOnly Property Version() As String
            Get
                Return "0.0.3.0"
            End Get
        End Property

    End Class

End Namespace

Namespace Models

    Public Class Blog

        Private mApplication As New Application

        Public ReadOnly Property Name() As String
            Get
                Return mApplication.Name
            End Get
        End Property

        Public ReadOnly Property URL() As String
            Get
                Return "http://www.26tp.com/post/Building-a-MVP-framework-Table-of-Contents.aspx"
            End Get
        End Property

    End Class

End Namespace

The Presenter

The AboutPresenter has the same requirements as AppWindowPresenter we created in Part 2 so we refactor to create the IPresenter, IView & IModel interfaces.

Public Interface IPresenter(Of TModel As IModel, TView As IView)

    ReadOnly Property View() As TView
    ReadOnly Property Model() As TModel

End Interface

Public Interface IModel
End Interface

Public Interface IView
    Sub BindData()
End Interface

I’ve also written our first abstract class.

Public MustInherit Class Presenter(Of TModel As IModel, TView As IView)
    Implements IPresenter(Of TModel, TView)

    Private mView As TView
    Private mModel As TModel

    Public Sub Initialize(ByVal model As TModel, ByVal startupView As TView)

        mModel = model.MustNotBeNull("model")
        mView = startupView.MustNotBeNull("startupView")

        mView.BindData()

    End Sub

    Public ReadOnly Property Model() As TModel Implements IPresenter(Of TModel, TView).Model
        Get
            Return mModel
        End Get
    End Property

    Public ReadOnly Property View() As TView Implements IPresenter(Of TModel, TView).View
        Get
            Return mView
        End Get
    End Property

End Class

The new AboutPresenter is down to 7 lines of code.

Imports TwentySixTP.MVP

Public Class AboutPresenter
    Inherits Presenter(Of AboutViewModel, AboutView)

    Public Sub New()
        Me.Initialize(New AboutViewModel, New AboutView(Me))
    End Sub

End Class

The View

The AboutView was created with the Form Designer following these steps:

  1. Add AboutViewModel to Data Sources.
  2. Drag AboutViewModel from Data Sources on to form.
  3. Delete the generated text boxes.
  4. Update the binding property of each remaining label.

Now the form has been designed we need the following code behind:

Public Class AboutView
    Implements TwentySixTP.MVP.IView

    Private mPresenter As AboutPresenter

    Public Sub New(ByVal presenter As AboutPresenter)
        MyBase.New()
        Me.InitializeComponent()
        mPresenter = presenter
    End Sub

    Public Sub BindData() Implements TwentySixTP.MVP.IView.BindData
        Me.AboutViewModelBindingSource.DataSource = mPresenter.Model
    End Sub

    Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK_Button.Click
        Me.Close()
    End Sub

    Private Sub BlogLinkLabel_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles BlogLinkLabel.LinkClicked
        System.Diagnostics.Process.Start(mPresenter.Model.Blog.URL)
    End Sub

End Class

Showing Aboutview

It is the responsibility of the Application view, sorry presenter in this MVP world, to show the AboutView on startup.

Public Class AppWindowPresenter
    Inherits Presenter(Of AppWindowViewModel, AppWindow)

    Public Sub New()
        Me.Initialize(New AppWindowViewModel, New AppWindow(Me))

        mStartupView.ShowAboutView()

    End Sub

End Class

The Unit Tests

We have a problem Houston. Actually we have two problems.

Problem 1

The AboutView is now being displayed during our tests. Not only is this visually annoying as AboutView is a dialog it stops the tests waiting for our response. What will the answer be????

Mock is the answer, of course!!! I’m relatively new to mocking so sorry if I’ve done some stupid.

<TestMethod()> Public Sub StartupForm_ReturnsAppWindow()

    ' Arrange
    ' ---------------------------------------------------------------------
    Dim mock = New Moq.Mock(Of AppWindowPresenter)
    mock.Setup(Function(m As AppWindowPresenter) m.ShowAboutView())
    mock.CallBase = True

    Dim presenter = mock.Object

    ' Act
    ' ---------------------------------------------------------------------
    Dim actual = presenter.View

    ' Assert
    ' ---------------------------------------------------------------------
    actual.ShouldNotBeNull()
    actual.ShouldBeInstance(Of AppWindow)()

End Sub

The above “mocks” the AppWindowPresenter and in particular it stubs out the ShowAboutView method so the dialog does not get displayed nor pause our unit tests.

Problem 2

The unit test for AboutView.BindData fails.

<TestMethod()> Public Sub AboutView_BindsData()

    ' Arrange
    ' ---------------------------------------------------------------------
    Dim presenter = New AboutPresenter

    ' Act
    ' ---------------------------------------------------------------------
    Dim frm = presenter.View()

    ' Assert
    ' ---------------------------------------------------------------------
    frm.BlogLinkLabel.Text.ShouldEqual(presenter.Model.Blog.Name)

End Sub

The test fails on the last line. Binding has been setup but .NET has not changed the value of frm.BlogLinkLabel.Text yet, seems not to do it until the form is actual displayed.

After some research it seems the only way for the test to work as is to show the form in the unit test. This works but I’m not sure it’s the best way to go about it. On reflection what is it we are trying to test. Can we trust that Microsoft have tested their binding framework? I think so. What we actually want to test is have we set the binding up correctly. Well of course we have if we used the designer’s drag and drop. But what if we change a property name? Because the designer uses “magic strings” it won’t recognise the refactoring. So what we need to do is test the definition of the bindings is correct. I’ll write a blog dedicated to this problem and its solution.

Source Code

Source code is available for this post via SVN:

svn export https://26tp.svn.codeplex.com/svn/tags/Building a MVP Framework - Part 3

Or to get the latest copy of the complete series code:

svn export https://26tp.svn.codeplex.com/svn/trunk/

View the 26tp.Examples.MVP project via the 26tp.Examples.sln solution.

Summary

In this post we did some refactoring to create our first interfaces and abstract class. We also introduced some mocking. Finally we found a problem unit testing data binding.

The next post, Create SQLite database from a SQL file, will look at a presenter controlling a collection of forms.

Building a MVP Framework: Part 2: The Application Window

Tim Murphy on Friday, January 8, 2010 12:10 PM

In Part 1 of series I said there would be five forms; in fact there are six. I forgot about the application window. The application window needs a title and link in the status bar to this series’ table of contents.

In Part 2 of the  series I’m going to design and implement a simple Welcome Dialog. Personally I hate Welcome Dialogs but its nice and simple to get the ball rolling.

The dialog will display:

  1. Application Name
  2. Version
  3. Web Link
  4. OK button

Application Startup

Later in this post I’ll describe the Presenter class but for now suffice to say it is god in an MVP application. That being the case we need to change the default startup routine of the WinForm project. Create a module with a public sub main:

Public Module Main

    Public Sub Main()

        Dim appWindowPresenter = New AppWindowPresenter

        Application.Run(appWindowPresenter.StartupForm)

    End Sub

End Module

The name of the module is not important but the procedure must be named Main.

Next go into the project properties and untick the Enable application framework option and select Sub Main as the Startup object (must be done in that order.

ApplicationStartup

The Presenter

As previously mentioned the Presenter is god. It is responsible for constructing the form, serving data to the form and responding to UI events.

Public Class AppWindowPresenter

    Private mStartupForm As AppWindow

    Public Sub New()
        mStartupForm = New AppWindow(me)
    End Sub

    Public ReadOnly Property StartupForm() As AppWindow
        Get
            Return mStartupForm
        End Get
    End Property

End Class

As you can see in the above code the AppWindowPresenter has a reference to the AppWindow form is constructs and AppWindow has a reference to AppWindowPresenter. This is required so when form receives a UI event it can talk to the presenter and the presenter can talk back to the window.

The Model

The AppWindow needs to set the text for it’s title and status bars; it does this via a Model. A Model is simple a data object, entity call it what you like. In this series of example class names ending with Model refer to object holding raw data from the database. Classes ending with ViewModel are objects tailor made for a View.

The AppWindowViewModel has just three readonly properties Name, BlogTitle and BlogURL.

Binding The Data

Now we have the model and the application window being displayed we need the application window to update itself with the values in the model. A quick change the presenter constructor:

Public Sub New()
    mModel = New AppWindowViewModel
    mStartupForm = New AppWindow(Me)
    mStartupForm.BindData()
End Sub

Followed by adding AppWindow.BindData() method:

Public Sub BindData()
    Me.Text = mPresenter.Model.Name
    Me.BlogToolStripStatusLabel.Text = mPresenter.Model.BlogTitle
End Sub

Now when the application starts the application title bar and status bar are updated with the required buttons.

The Unit Tests

So far I’ve not mention unit tests. Well of course I’ve been writing this as we go. As you can imagine the unit tests for the AppWindowPresenter and AppWindowViewModel class are very quick and easy to implement. The AppWindow tests are equally easy.

<TestClass()> Public Class AppWindow_Tests

    <TestMethod()> Public Sub CanConstruct()

        ' Arrange

        ' Act
        Dim value = New AppWindow(New AppWindowPresenter)

        ' Assert
        value.ShouldNotBeNull()

    End Sub

    <TestMethod()> Public Sub AppWindow_BindsData()

        ' Arrange
        Dim presenter = New AppWindowPresenter

        ' Act
        Dim frm = presenter.StartupForm()

        ' Assert
        frm.Text.ShouldEqual(presenter.Model.Name)
        frm.BlogToolStripStatusLabel.Text.ShouldEqual(presenter.Model.BlogTitle)

    End Sub

End Class

 

As you can see the we have been able to test every method in the AppWindow with very little pain or fanfare.

Source Code

Source code is available for this post via SVN:

svn export "https://26tp.svn.codeplex.com/svn/tags/Building a MVP Framework - Part 2"

Or to get the latest copy of the complete series code:

svn export https://26tp.svn.codeplex.com/svn/trunk/

View the 26tp.Examples.MVP project via the 26tp.Examples.sln solution.

Summary

In this post we can created our first Presenter, Model & Form classes along with accompanying unit tests. So far the framework has meet all the goals set out in Part 1.

Has it been worth it. It would have been quicker and easy just to design the AppWindow in Visual Studio, use the properties window to set the required values and we could write unit tests for this. But one positive side effect is we can easily read the code to see exactly have properties have been set. Using the MVP pattern we are able to create the AppWindow with the designer and the only properties we change in the designer is the name of the controls.

In the next post, The About Dialog, we will look at using a BindingSource to bind the model to the form.

Building a MVP Framework: Part 1: The Goals

Tim Murphy on Thursday, January 7, 2010 10:43 PM

In Part 1 of the Building a MVP Framework series I’m going to define the goals of the sample application and the framework itself.

Framework Goals

The goals of any Framework are (or at least should be) determined by the requirements of the applications that will use the Framework. The MVP Framework must address the following needs.

  1. Separation of UI and Business Logic code.
  2. Thin UI.
  3. Easy to implement.
  4. Easy to Unit Test a very high percentage of the application.

Sample Application Goals

In this series I’ll be using the Chinook Database imported into a SQLite database. Initially the application will have five six forms.

  1. Application Window.
  2. Welcome Dialog.
  3. Create SQLite database from SQL file.
  4. Edit Artists. Very simple form allowing scrolling throw the list of Artists and editing.
  5. Grid of Artists. Very simple form display a grid of Artists with editing.
  6. Artists Explorer. The explorer will have three panels: Tree, Grid & Edit. The explorer will use and display data from the Artist, Album, Track, PlaylistTrack & Playlist tables of the Chinook Database.

In Part 2, The Application Window, I’ll look at the first implementation of MVP.

Building a MVP Framework: Table of Contents

Tim Murphy on Thursday, January 7, 2010 9:19 PM

Lately I’ve spent a reasonable amount of time with ASP.NET MVC and grown to love the pattern.  My current project is a Windows Form application and in need of help from a similar. Having spent the last two days researching the topic I’ve not been able to find a framework that I’m satisfied. Largely they are under documented or overly complex. I’m trying to make development easier that harder!

To quote a Scott Hansleman blog post I read recently “If you don't like something, fix it.”. I have a very pressing need and short deadline for the current project. In other words to begin with it’s going to be a hack fest. The main concern will be to get the job done, worry about the pretty stuff later on.

Wish me luck.

Table of Contents

  1. The Goals
  2. The Application Window
  3. The About Dialog

Example: Implementing INotifyPropertyChanged

Tim Murphy on Sunday, January 3, 2010 5:43 PM

In the previous example DataGridView we had a very simple Models.Contact class being displayed in a DataGridView. In this post we will look at how and why to implement INotifyPropertyChanged interface on the Models.Contact.

Why

It is time to be generous and give away some cars :-)

Private Sub GiveCarButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GiveCarButton.Click

    Dim contact As Models.Contact = Me.ContactBindingSource.Current

    If contact Is Nothing Then

        MessageBox.Show("Must be on a current record to give away a car.", "Car Give Away", MessageBoxButtons.OK, MessageBoxIcon.Information)

    End If

    If MessageBox.Show(String.Format("Give a car away to '{0}'?", contact.FullName), "Car Give Away", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then

        contact.Cars += 1

    End If

End Sub

Pretty simple but the grid/edit window that raises this event will not display the new number cars. Updating Models.Contacts to implement the INotifyPropertyChanged interface will fix this problem.

How

Imports System.ComponentModel

Namespace Models

    Public Class Contact
        Implements INotifyPropertyChanged

        Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        ...

        Private mCars As Integer
        Public Property Cars() As Integer
            Get
                Return mCars
            End Get
            Set(ByVal value As Integer)
                If mCars <> value Then
                    mCars = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Cars"))
                End If
            End Set
        End Property

        ...

    End Class

End Namespace

 

With the above change to our property any control that is bound to Models.Contact will be aware of any changes made to the underlying data. We need to update every property and in the codes current form that would be time consuming and error prone. A future post will look at refactoring Models.Contact.

Example

The example can be downloaded from http://26tp.codeplex.com, project 26tp.Examples.ImplementINotifyPropertyChanged.

This example has two forms, Grid & Edit. Each form has a Notify Property Changes checkbox. Try giving a car away with the checkbox ticked and unticked to see the effect of raise the PropertyChanged event.

Example: DataGridView

Tim Murphy on Saturday, January 2, 2010 5:40 PM

This is the first in a series of examples on databinding, MVC and related matters using .NET Framework, Visual Studio 2008 & VB.NET.

The purpose of this example is to show how to quickly and easily develop a form with a DataGridView, Navigation and the ability to load data. It is purposely simplistic in nature and future examples will provide best practice and real world examples.

Source code for all examples can be found at http://26tp.codeplex.com. 26tp.Examples.DataGridView is the project for this blog.

Models.Contact

Models.Contact is a very simple class with the following properties:

  • FirstName
  • LastName
  • Birthday
  • Cars
  • Age: Read only, calculated from their birthday to today.
  • FullName: Read only, FirstName + “ “ + LastName.

Excerpt of class.

Namespace Models

    Public Class Contact

        Private mFirstName As String
        Public Property FirstName() As String
            Get
                Return mFirstName
            End Get
            Set(ByVal value As String)
                mFirstName = value
            End Set
        End Property

        ...

        Public ReadOnly Property Age() As Long?
            Get

                If Not mBirthday.HasValue Then
                    Return Nothing
                End If

                Dim years = Now.Year - mBirthday.Value.Year

                If New Date(mBirthday.Value.Year, Now.Month, Now.Day) < mBirthday.Value Then
                    years -= 1
                End If

                Return years

            End Get
        End Property

        Public ReadOnly Property FullName() As String
            Get
                Return CStr(mFirstName + " " + mLastName).Trim
            End Get
        End Property

    End Class

End Namespace

Creating the DataGridView

  1. Create a blank form.
  2. Add a StatusStrip.
  3. Add a Panel docked to the right. This will be used to host buttons and labels that will be created later.
  4. Add Models.Contact to Data Sources via the Data –> Add New Data Source menu option.
  5. Finally drag the Models.Contact Data Source onto the window and dock it into the Parent Container.

Hit F5 and you have a working DataGridView. FullName changes when you enter FirstName and/or LastName. Age changes when you enter a Birthday.

Load Existing Data

To load existing data the ContactBindingSource.DataSource needs to be updated.

Public Class Contacts

    Private mContacts As New BindingList(Of Models.Contact)

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)

        Me.ContactBindingSource.DataSource = mContacts

    End Sub

    Private Sub LoadButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LoadButton.Click

        mContacts = New BindingList(Of Models.Contact)((New Models.ContactRepository).FindAll)
        Me.ContactBindingSource.DataSource = mContacts

    End Sub

End Class

 

In the above code the mContacts was introduced as the container for our fake contacts. A BindingList is used so any changes we make to mContacts will be recognised by ContactBindingSource and shown in the data grid.

The ContactRepository.FindAll method simply returns a list of fake contacts.

Namespace Models

    Public Class ContactRepository

        Public Function FindAll() As List(Of Contact)

            Dim list = New List(Of Contact)

            list.Add(New Contact With {.FirstName = "Edna", .LastName = "Kimble", .Birthday = New Date(1957, 12, 24)})
            list.Add(New Contact With {.FirstName = "Maria", .LastName = "Fenimore", .Birthday = New Date(1946, 7, 7)})
            list.Add(New Contact With {.FirstName = "John", .LastName = "Bateman", .Birthday = New Date(1970, 2, 8)})
            list.Add(New Contact With {.FirstName = "James", .LastName = "Ferguson", .Birthday = New Date(1967, 7, 8)})
            list.Add(New Contact With {.FirstName = "Debra", .LastName = "Lyons", .Birthday = New Date(2010, 1, 1)})

            Return list

        End Function

    End Class

End Namespace

The next blog in the Examples series will look at implementing INotifyPropertyChanged.

Code Snippet for Test Methods

Tim Murphy on Saturday, January 2, 2010 7:46 AM

I previously blog that Arrange Act Assert Comments help focus your unit test. Now for a code snippet to help implement that idea.

<?xml version="1.0" encoding="UTF-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Define a Test Method</Title>
      <Author>Tim Murphy</Author>
      <Description>Defines a Test Method with AAA comments.</Description>
      <Shortcut>TM</Shortcut>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>Method</ID>
          <Type>String</Type>
          <ToolTip>Replace this with method to be tested.</ToolTip>
          <Default>Method</Default>
        </Literal>
        <Literal>
          <ID>Expects</ID>
          <Type>String</Type>
          <ToolTip>Replace this with what the method is expected to do.</ToolTip>
          <Default>Expects</Default>
        </Literal>
        <Object>
          <ID>When</ID>
          <Type>String</Type>
          <ToolTip>Replace this with the condition the method is testing.</ToolTip>
          <Default>When</Default>
        </Object>
      </Declarations>
      <Code Language="VB" Kind="method decl">
          <![CDATA[<TestMethod()> Public Sub $Method$_$Expects$_$When$()

        ' Arrange
        ' ---------------------------------------------------------------------

        ' Act
        ' ---------------------------------------------------------------------

        ' Assert
        ' ---------------------------------------------------------------------

    End Sub]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

 

After installing the snippet just type tm then tab and you get:

<TestMethod()> Public Sub Method_Expects_When()

    ' Arrange
    ' ---------------------------------------------------------------------

    ' Act
    ' ---------------------------------------------------------------------

    ' Assert
    ' ---------------------------------------------------------------------

End Sub