GoToTest macro for VisualStudio

4

When you are doing Test Driven Development (TDD) you are constantly switching back and forth between production code and test classes.

As it’s good idea to have those files in separate projects, it’s sometimes very hard to find test code for a class and vice-versa.

Some time ago I decided to write a simple Visual Studio macro, that solves this problem.
It’s based on the convention that all your test classes have Test or Tests suffix (e.g. CSVReader.cs and CSVReaderTests.cs)

I’m far from being VB expert, so the code is not perfect (Why does VS use Visual Basic for Macros?):

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Imports System.IO

Public Module MainModule
    Dim _patterns As String() = {"{0}Test", "{0}Tests"}
    Dim _reversePatterns As String() = {"Tests", "Test"}

    Sub GoToTest()
        If DTE.SelectedItems.Count = 0 Then
            Return
        End If

        Dim fullFileName As String = DTE.ActiveDocument.Name
        Dim fileName As String = Path.GetFileNameWithoutExtension(fullFileName)
        Dim extension As String = Path.GetExtension(fullFileName)

        If IsTestFile(fileName) Then
            For Each reversePattern As String In _reversePatterns
                If fileName.Contains(reversePattern) Then
                    TryOpen(fileName.Replace(reversePattern, "") + extension)
                End If
            Next
        Else
            For Each pattern As String In _patterns
                TryOpen(String.Format(pattern, fileName) + extension)
            Next pattern
        End If
    End Sub

    Function IsTestFile(ByVal fileName As String)
        For Each reversePattern As String In _reversePatterns
            If fileName.Contains(reversePattern) Then Return True
        Next
        Return False
    End Function

    Function TryOpen(ByVal fileName As String) As Boolean
        Dim item As EnvDTE.ProjectItem
        item = FindItem(FileName)
        If Not (item Is Nothing) Then
            OpenItem(item)
            Return True
        End If
        Return False
    End Function

    Function FindItem(ByVal fileName As String) As EnvDTE.ProjectItem
        If String.IsNullOrEmpty(fileName) Then
            Return Nothing
        End If
        Dim item As EnvDTE.ProjectItem = DTE.Solution.FindProjectItem(fileName)
        Return item
    End Function

    Sub OpenItem(ByVal item As EnvDTE.ProjectItem)
        item.Open()
        item.Document.Activate()
    End Sub

End Module

How to install:

Start Visual Studio and go to Tools/Macros/Load Macro Project…:
1

Right click on the Visual Studio toolbar, and select customize:
2

Select ‘Macros’ category and find ‘GoToTest’ Macro:
3

Of course you can change the name of the button.
4

And finally the macro itself:
GotoTestMacro

My ReSharper templates for Unit Testing

As I always have problem synchronizing my office and home machine’s templates I thought this would be good place to store them.

Inline templates (LiveTemplates.xml)

test

[Test]
public void Method_Condition_Result()
{
	$END$
}

setup

[SetUp]
public void SetUp()
{
	$END$
}

record

using(mocks.Record())
{
	$END$
}

play

using(mocks.Playback())
{
	$END$
}

File templates (FileTemplates.xml)

NUnitTestFile

using NUnit.Framework;

namespace $Namespace$
{
    [TestFixture]
    public class $FileName$
    {
        [Test]
        public void Method_Condition_Result()
        {
        }
    };
}

FileTemplates
LiveTemplates

Assemblers with ConvertAll

In most applications we have some kind of assembler classes, that create models used in the presentation layer:

public class EntityComboModelAssembler
{
   public List<comboBoxModel> CreateComboBoxModels(IList<entity> entities)
   {
      List<comboBoxModel> list = new List<comboBoxModel>();
      foreach (Entityentity in entities)
      {
         list.Add(new ComboBoxModel(entity, entity.Name));
      }
      return list;
   }
}

It is possible to write this code in 1 (one) line of code:

public class EntityComboModelAssembler
{
   public List<comboBoxModel> CreateComboBoxModels(IList<entity> entities)
   {
      return entities.AsList().ConvertAll(x => new ComboBoxModel(x, x.Name));
   }
}

We can also remove AsList method by using extension method that adds ConvertAll method to IList interface:

public static class IListExtensions
{
   public static List<tout> ConvertAll<tin, TOut>(this IList<tin> source, Converter<tin, TOut> converter)
   {
      return source.ToList().ConvertAll(converter);
   }
}

And the final code:

public class EntityComboModelAssembler
{
   public List<comboBoxModel> CreateComboBoxModels(IList<entity> entities)
   {
      return entities.ConvertAll(x => new ComboBoxModel(x, x.Name));
   }
}

Introducing PostSharp to your team

PostSharp is AOP (Aspect-Oriented-Programming) framework (www.sharpcrafters.com).
It transparently inserts itself in the build process and post-processes the compiled assembly.

To enable PostSharp in your project you need to download and run the PostSharp installer, and add appropriate references in your project.

However if your project is in SVN, and you are not the only one compiling it (other developers, CI machine) most likely you don’t want to run PostSharp installer on all the machines.

There is a way to introduce PostSharp transparently to your project.

  • Put all the PostSharp files in the LibPostSharp folder of your project and add this folder to SVN.
  • Modify the .csproj file: After the last ItemGroup following xml must be inserted:
  <propertyGroup>
    <dontImportPostSharp>True</dontImportPostSharp>
    <!-- Add the next line if you are using Visual Studio 2010 -->
    <!-- <postSharpUseCommandLine>True</postSharpUseCommandLine>-->
  </propertyGroup>
  • Modify the .csproj file: After the
<import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />

following xml must be inserted:

<import Project="..LibPostSharpPostSharp.targets" />

The whole change should be similar to this sample:

</itemGroup>
  <propertyGroup>
    <dontImportPostSharp>True</dontImportPostSharp>
  </propertyGroup>
  <import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />
  <import Project="..LibPostSharpPostSharp-1.5.targets" />
</project>

INotifyPropertyChanged with PostSharp 1.5

If you are doing WPF development, most likely you are tired of writing property implementations that raise PropertyChanged event manually:

public class MainWindowViewModel : ViewModel
{
    private string _message;

    public string Message
    {
        get
        {
            return _message;
        }
        set
        {
            _message = value;
            OnPropertyChanged("Message");
        }
    }

    // ...
}

PostSharp is a great tool to make such things simplier.

Let’s look at the specific ViewModel class that has a Message property that is bound to some UI element using XAML:

public class MainWindowViewModel : ViewModel
{
    [RaisePropertyChanged]
    public string Message { get; set; }

    // ...
}

Notice the RaisePropertyChanged attribute, which we’ll implement later.

Here’s our base ViewModel class that provides actual implementation of the INotifyPropertyChanged interface:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged
        = delegate { };

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
    }
};

Finally the PostSharp attribute:

[Serializable]  // required by PostSharp
public class RaisePropertyChangedAttribute : OnMethodBoundaryAspect
{
    private string _propertyName;

    /// <summary>
    /// Executed at runtime, after the method.
    /// </summary>
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
        ViewModel viewModel = (ViewModel)eventArgs.Instance;
        viewModel.OnPropertyChanged(_propertyName);
    }

    public override bool CompileTimeValidate(MethodBase method)
    {
        if (IsPropertySetter(method))
        {
            _propertyName = GetPropertyName(method);
            return true;
        }
        return false;
    }

    private static string GetPropertyName(MethodBase method)
    {
        return method.Name.Replace"set_", "");
    }

    private static bool IsPropertySetter(MethodBase method)
    {
        return method.Name.StartsWith("set_");
    }
};

Note that we are validating if the method is in fact a property only during compilation time using CompileTimeValidate method.

During compile time appropriate invocations of OnPropertyChanged method will be injected after every set operation applied to the Message property.