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
   6:  
   7: Public Module CodeEditor
   8:     Private Const INDENT_SIZE As Integer = 4
   9:  
  10:     Public Sub ReplaceSpacesInTestNameWithUnderscores()
  11:         If DTE.ActiveDocument Is Nothing Then Return
  12:         Dim wrCS As Boolean = DTE.Properties("TextEditor", "CSharp").Item("WordWrap").Value
  13:  
  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
  21:  
  22:             selection.SelectLine()
  23:             If selection.Text = "" Then Return
  24:  
  25:             Dim methodIndex As Integer = selection.Text.IndexOf("public void ")
  26:             Dim classIndex As Integer = selection.Text.IndexOf("public class ")
  27:  
  28:             If (methodIndex < 0 AndAlso classIndex < 0) Then
  29:                 selection.MoveToLineAndOffset(line, offset)
  30:                 Return
  31:             End If
  32:  
  33:             index = CType(IIf(methodIndex >= 0, methodIndex, classIndex), Integer)
  34:  
  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
  46:  
  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
  53:  
  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
  60:  
  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
  69:  
  70:     Private Function AddIndent(ByVal spacer As Char) As String
  71:         Return String.Empty.PadRight(INDENT_SIZE, spacer)
  72:     End Function
  73:  
  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
  87:  
  88:     Private Function GetWhiteSpace(ByVal selection As TextSelection, ByVal index As Integer) As String
  89:         Return selection.Text.Substring(0, index)
  90:     End Function
  91:  
  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.