I’ve been using a BDD macro based on the one JP Boodhoo published for getting more descriptive test names along with a R# template so that I can type “test” + tab, type the test name (with spaces) and then hit Alt+- and voila – BDD style test names.

This worked well for a while until some point where R# started to do autocompletion on the test name as I typed (for instance, typing ‘Check that blah is not null’ often ended up as ‘Check that blah is NotImplementedException null’). That was OK though after a quick rework of the R# template and macro so R# wraps the test name in quotes which the macro then removes.

But R# doesn’t format the end result as well (the method braces don’t indent correctly) and this bugged me for a while.

So, a quick change to the macro and the R# template and now it’s much more smooth. Typing ‘tests’ + tab and R# outputs public class “FIXTURENAME” (with fixturename highlighted and ready to type over). Type out the name of the fixture, Alt+- and the macro replaces spaces, puts in the [TestFixture] attribute, drops in the opening and closed braces and puts the caret right where I want it. Sweet.

Do the same with ‘test’ + tab, type test name, Alt+- and you get the same for a test. Extra sweet.

This is the BDD macro:

   1: Imports System
   2: Imports System.Windows.Forms
   3: Imports EnvDTE
   4: Imports EnvDTE80
   5: Imports System.Diagnostics
   7: Public Module CodeEditor
   8:     Private Const INDENT_SIZE As Integer = 4
  10:     Public Sub ReplaceSpacesInTestNameWithUnderscores()
  11:         If DTE.ActiveDocument Is Nothing Then Return
  12:         Dim wrCS As Boolean = DTE.Properties("TextEditor", "CSharp").Item("WordWrap").Value
  14:         Try
  15:             DTE.Properties("TextEditor", "CSharp").Item("WordWrap").Value = False
  16:             Dim selection As TextSelection = CType(DTE.ActiveDocument.Selection(), EnvDTE.TextSelection)
  17:             Dim textPoint As TextPoint = CType(selection.ActivePoint, TextPoint)
  18:             Dim line As Integer = textPoint.Line
  19:             Dim offset As Integer = textPoint.LineCharOffset
  20:             Dim index As Integer
  22:             selection.SelectLine()
  23:             If selection.Text = "" Then Return
  25:             Dim methodIndex As Integer = selection.Text.IndexOf("public void ")
  26:             Dim classIndex As Integer = selection.Text.IndexOf("public class ")
  28:             If (methodIndex < 0 AndAlso classIndex < 0) Then
  29:                 selection.MoveToLineAndOffset(line, offset)
  30:                 Return
  31:             End If
  33:             index = CType(IIf(methodIndex >= 0, methodIndex, classIndex), Integer)
  35:             If methodIndex >= 0 Then
  36:                 HandleTestMethod(selection, index)
  37:             Else
  38:                 HandleTestClass(selection, index)
  39:             End If
  40:         Catch ex As Exception
  41:             MsgBox(ex.Message)
  42:         Finally
  43:             DTE.Properties("TextEditor", "CSharp").Item("WordWrap").Value = wrCS
  44:         End Try
  45:     End Sub
  47:     Private Sub HandleTestClass(ByVal selection As TextSelection, ByVal index As Integer)
  48:         Dim whiteSpace As String = GetWhiteSpace(selection, index)
  49:         HandleTestNameFormatting(selection, "public class ", index)
  50:         AddAttribute(selection, index, "[TestFixture]", whiteSpace)
  51:         AddBraces(selection, index, whiteSpace)
  52:     End Sub
  54:     Private Sub HandleTestMethod(ByVal selection As TextSelection, ByVal index As Integer)
  55:         Dim whiteSpace As String = GetWhiteSpace(selection, index)
  56:         HandleTestNameFormatting(selection, "public void ", index)
  57:         AddAttribute(selection, index, "[Test]", whiteSpace)
  58:         AddBraces(selection, index, whiteSpace)
  59:     End Sub
  61:     Private Sub AddAttribute(ByVal selection As TextSelection, ByVal index As Integer, ByVal attribute As String, ByVal whiteSpace As String)
  62:         selection.LineUp()
  63:         selection.SelectLine()
  64:         If selection.Text.Trim <> attribute Then
  65:             selection.Insert(selection.Text & whiteSpace & attribute & vbCrLf)
  66:         End If
  67:         selection.LineDown()
  68:     End Sub
  70:     Private Function AddIndent(ByVal spacer As Char) As String
  71:         Return String.Empty.PadRight(INDENT_SIZE, spacer)
  72:     End Function
  74:     Private Sub AddBraces(ByVal selection As TextSelection, ByVal index As Integer, ByVal whiteSpace As String)
  75:         selection.SelectLine()
  76:         If selection.Text.Trim <> "{" Then
  77:             selection.Insert(String.Format("{0}{4}{1}{0}{2}{1}{0}{5}{1}{3}", whiteSpace, vbCrLf, AddIndent(whiteSpace(0)), selection.Text, "{", "}")) ' & whiteSpace & AddIndent(whiteSpace(0)) & vbCrLf & "}" & vbCrLf)
  78:             selection.LineUp()
  79:             selection.LineUp()
  80:             selection.LineUp()
  81:             selection.EndOfLine()
  82:         Else
  83:             selection.LineUp()
  84:             selection.EndOfLine()
  85:         End If
  86:     End Sub
  88:     Private Function GetWhiteSpace(ByVal selection As TextSelection, ByVal index As Integer) As String
  89:         Return selection.Text.Substring(0, index)
  90:     End Function
  92:     Private Sub HandleTestNameFormatting(ByVal selection As TextSelection, ByVal prefix As String, ByVal index As Integer)
  93:         Dim whiteSpace As String = GetWhiteSpace(selection, index)
  94:         prefix = whiteSpace + prefix
  95:         Dim description As String = selection.Text.Replace(prefix, String.Empty).Trim
  96:         If description(0) = """" Then
  97:             Dim lastQuote As Integer = description.LastIndexOf("""")
  98:             If lastQuote = 0 Then
  99:                 description = description.Substring(1)
 100:             Else
 101:                 Dim endOfDescription As String = description.Substring(lastQuote + 1)
 102:                 description = description.Substring(1, lastQuote - 1) + endOfDescription
 103:             End If
 104:         End If
 105:         Dim text As String = prefix + description.Replace(" ", "_").Replace("'", "_") + vbCrLf
 106:         selection.Delete()
 107:         selection.Insert(text)
 108:         selection.LineUp()
 109:     End Sub
 110: End Module

For the ReSharper templates, open up the live templates and add a template called ‘tests’ with the body of template as below:

public class "$FIXTURENAME$"

And then another one called ‘test’ with the body as

public void "$TESTNAME$"()

Mark both of these as available for C# only (or if for VB.Net, adjust the template body for the VB equivalent), and for the ‘test’ one, mark this as available only where a method declaration is allowed.

That’s it. Now you can get into the flow of typing ‘test’ TAB ‘name of test’ Alt+- and then typing in the test itself.

I have a confession to make. I’m a Resharper junkie. I’m addicted to it and just can’t work with a naked Visual Studio any more. I’ve tried hiding this since the early 1.something days (1.65? it’s a bit of a blur) but I can’t any more.

OK – anyone that knows me will know that I haven’t hidden this fact very well and that in fact I try to spread my addiction to others. Well now I have good news. Jetbrains have a partner program and if you drop me your details then I can arrange for a 60 day trial of Jetbrains products and if you do purchase then you will get between 5%-10% off the purchase price.

Obviously I will benefit in some way beyond the simple fact that I have helped spread the joy to others but let’s face it – that is reward in itself 😉

OK – it has been a long long LONG time coming but I have finally bitten the bullet and got a blog. The thing that finally motivated me enough is that I’m doing a talk at the Cambridge SPA group on June 11th about Dependency Injection (and will hopefully get the chance to give the same talk at Skills Matter).

So, that is the motivating reason for the blog. I now plan to follow up this starting post with many others.