June 2006 - Posts

Checking for mocked parameters in Natural Mock expectations

TypeMock nicely introduced the Check.IsMock functionality with their 3.1.4 release and this ties in very nicely with the Natural Mock stuff they've introduced in their soon to be released 3.5.0.

e.g. we have a production method which uses a singleton's CommandManager object to ExecuteCommand using a new InitialisationCommand but we want to mock the whole call including the new InitialisationCommand:

public void Foo()

{

   this.initialised = UIPManager.Instance.CommandManager.ExecuteCommand(new InitialisationCommand());

}

So code up a testcase as follows:

[Test]

public void FooTest_RecorderManager()

{

   Mock mockInitialisationCommand = MockManager.Mock(typeof (InitialisationCommand), Constructor.Mocked); //(1)

   using (RecordExpectations recordExpectations = RecorderManager.StartRecording()) //(2)

   {

      recordExpectations.ExpectAndReturn(UIPManager.Instance.CommandManager.ExecuteCommand(null), false);

      recordExpectations.CheckArguments(Check.IsMock(mockInitialisationCommand)); //(3)

   }

   this.vFrameworkEntryPoint.Foo();

   Assert.IsFalse(this.vFrameworkEntryPoint.Initialised);

}

(1) We create a mock to handle the creation of the new InitialisationCommand

(2) Using a Natural Mock we configure it to expected a call to 'UIPManager.Instance.CommandManager.ExecuteCommand' and return false.

(3) We configure the Natural Mock to check that the argument provided in the ExpectAndReturn we just configured is the mock of the new InitialisationCommand.

//NB// You will also notice that we are now using the object model directly within the test (UIPManager. and so on) so our tests automatically become more resilient to refactoring work - if, for example, the ExecuteCommand is renamed or the signature altered the solution will fail to compile until the tests have been updated. Big Bonus! (If you use a tool like Resharper then you could have it renamed for you)

TypeMock.Net 3.5.0 Preview

TypeMock.Net have been kind enough to send me a pre-release of their latest release at v3.5.0.

There are several new features including improved support for usage within the Visual Studio .Net 2005 IDE but the new toy that I can say interests me the most with this new release is Natural TypeMock (TM).

I have been struggling recently with a solution which started life as a test driven project but the project ran into difficulties and the approach was discarded for a while - however, it is now desirable for them to resurrect and apply unit testing to their solution (see my previous rant). The problem occurs with the prolific use of static contructors on loggers and other such objects some of which are singletons to boot. To date I have been forced to construct elaborate mocking objects to be able to mock these calls - this has entailed the usual method of setting expectations using a string to match the method/getter etc - this is where Natural TypeMock comes into its own.

With Natural TypeMocks you can code up the unit test using the context sensitive help of the ide (sweet!) so the compile will fail if you refactor the production classes without adjusting the mocks - nice, heh!

The magic happens using RecordExpectations on the RecordManager:

using (RecordExpectations recorder = RecorderManager.StartRecording())
{
    Logger.Log(Logger.NORMAL,null);
    Logger.Log(Logger.NORMAL,null);
}

You set the expectations within the block by recording the actions you're expecting during the test run - very tidy.

The arguments are checked if they are non-null values so to check for specific arguments to the logging functions:

using (RecordExpectations recorder = RecorderManager.StartRecording())
{
    Logger.Log(Logger.NORMAL,"First expected log message");
    Logger.Log(Logger.NORMAL,"Second expected log message");
    recorder.CheckArguments();
}



To check arguments and return an expected value is just as simple:

using (RecordExpectations recorder = RecorderManager.StartRecording())
{
    recorder.ExpectAndReturn(Logger.Log(Logger.NORMAL,"First expected log message"), false);
}



I will be pitting the new features of TypeMock against the solution tomorrow so will follow up with any problems/solutions that I encounter along the way!

Keep an eye out for it at TypeMock website.

Rant :: Commitment, discipline and practice *will* reap rewards

I have seen this happen a few times in the last couple of months when new developers have been introduced to TDD.

Initially, enthusiasm is high at trying something new but after a few short days these guys can dismiss it out-of-hand because 'it doesn't work'. They find that writing tests for their code is difficult so conclude the technique is flawed.

In 99.9 cases out of 100 this is because these guys have missed the point - TDD is about using the process to help you design and produce a testable code base by creating tests first to help shape your design and make it testable.

My experience has taught me that you can usually create the tests you need quickly and simply - if the tests are complicated and taking a long time it's a pretty good indicator that you are heading in the wrong direction.

A point in fact is logging - loadsa folks love to have a logging manager which is either (1) a singleton and then they spatter their code with Logger.Instance.Log() calls or (2) a static constructor of Logger logger = new Logger(blah, blah).

I have no objection to singletons per-se but this is a clear example of tightly coupling classes together because you can't have an instance of your class without requiring a Logger to be setup and configured - this could be considered fine for production but a real pain in the @ss for testing! If the test had been written first the coder would quickly have realised that this is a bad idea since they would have to produce code to setup and configure a logger before doing anything else and then do all the required housekeeping to tidy up after it when the test is complete. Add to that the possibility that the logger may have bugs which could cause false negatives in the tests and you know you're already in trouble...

If you have an ILogger interface and allow an instance implementing ILogger to be supplied to your class all this hassle goes away - you can instantiate your production class in the test fixture and just squirt in an interface mock of ILogger using TypeMock, NMock, RhinoMock or any other mocking gizmo you care to try. (For any typemockers out there - I know TypeMock supports MockAll which can intercept calls to singletons but it still leaves you with tightly-coupled code).

This can be compatible with singletons too - if your singleton implements ILogger that is. Logger.Instance could be supplied to your class in production just as easily as an interface mock or any other ILogger implementation for that matter == loose coupling == more flexible code which can be re-used.

TDD is a design process in my book and takes commitment, practice and discipline.

It's tempting to switch off a few problematic tests when the deadlines are tight - but how can you be confident that there are no breaking changes (and why bother in the first place)?

It's not something you just sit down and do straight off the bat - you need to practice and you will get better and faster. You will spot pitfalls and avoid mistakes - you will also find yourself adopting testable  tried-and-tested (no pun intended) design patterns instead of re-inventing the wheel every 3 weeks.

It's very easy to find yourself writing code and saying "I'll implement a test for that in 5 minutes" - so you bung a MyMethodTest(){} in your test fixture with an [Ignore("")] attribute for the time being with all the best intentions of going back to it. NUnit gui is littered with yellow blobs with no explanation of why - I have fallen into this myself in the past and those few tests I needed to write in a few minutes added up to a weekend's work that I would much rather have spent on Bournemouth beach! A lesson hard learned....

Now I've got that off my chest I think I will have a nice cup of Columbia's finest...

Enabling TypeMocking for NUnit Gui the easy way

To use TypeMock in your unit tests you need to enable the typemocking engine.

TypeMock's documentation cites a few different ways to do this but doesn't provide an easy way to run the NUnit Gui alongside Visual Studio (which is the way I prefer to work) without running batch files which I find cumbersome.

Solution:

1) Be sure to install the Visual Studio AddIn when you install TypeMock - this should give you someTypeMock options in the Tools menu.

2) Enable TypeMock in Visual Studio using the 'Enable TypeMock.Net' option in the 'Tools' menu (if it reads 'Disable TypeMock.Net' then it's already enabled - which is the install default)

3) Create an external tools link to the NUnit Gui in the 'Tools' menu 'External Tools' option to launch the gui.

If you want to run TypeMock'ed unit tests just be sure to launch the gui from the 'Tools' link you created and TypeMocking is enabled! You can now run your unit tests in the gui in exactly the same way as normal.

NB// Remember you can attach to the NUnit Gui process from Visual Studio if you want to debug your unit tests.

Visual Studio IDE Keyboard Shortcuts Macro

I came across this macro last week and have found it to be practically invaluable in my quest to become the keyboard shortcut Ninja!

It works for 2003 and 2005 so get to it!

Kudos to Jeff Atwood @ http://www.codinghorror.com/blog for this peach.

 

Imports EnvDTE

Imports System

Imports System.Diagnostics

Imports System.Text

Imports System.Text.RegularExpressions

Imports System.Data

Imports System.IO

Imports System.Environment

 

'''

''' Keyboard Shortcut enumeration macro for VS 2005

'''

''' Jeff Atwood

''' http://www.codinghorror.com/blog/

'''

''' version 3/2/06, 3pm

'''

Public Module KeyboardShortcuts

'-- possible fields are Category, Command, Scope, PrimaryKey, Key

Private Const _defaultSortOrder As String = "Scope, PrimaryKey, Key"

Private Const _groupColumnName As String = "Scope"

Private _pageTitle As String

Private _fileName As String

Private _sb As New Text.StringBuilder

Sub List()

_pageTitle = "Visual Studio .NET " & VSNetYear() & " Keyboard Shortcuts"

_fileName = _pageTitle & ".htm"

Dim filePath As String

filePath = Path.Combine(GetFolderPath(SpecialFolder.Personal), _fileName)

If Not File.Exists(filePath) Then

GenerateDocument(filePath)

End If

Debug.WriteLine(DTE.Version)

DTE.ItemOperations.Navigate(filePath)

End Sub

Private Function VSNetYear() As String

If DTE.Version.StartsWith("8") Then

Return "2005"

End If

If DTE.Version.StartsWith("7.1") Then

Return "2003"

End If

If DTE.Version.StartsWith("7") Then

Return "2002"

End If

Return "20xx"

End Function

Private Sub GenerateDocument(ByVal filePath)

Dim dv As New DataView(KeyboardCommandTable())

dv.Sort = _defaultSortOrder

WriteLine("<html>")

WriteLine("<head>")

WriteLine("<title>" & _pageTitle & "</title>")

WriteLine("<style type=""text/css"">")

WriteLine("body {font-family: Tahoma}")

WriteLine("table {font-size: 80%}")

Write(".kbd {font-family:arial,helvetica,sans-serif;padding:5px 3px;white-space:nowrap;")

Write("color:#000;background:#eee;border-width:2px 4px 5px 3px;")

WriteLine("border-style:solid;border-color:#ccc #aaa #888 #bbb;}")

WriteLine(".i20 {margin:8px 10px;}")

WriteLine("</style>")

WriteLine("</head>")

WriteLine("<body>")

WriteLine("<h2>" & _pageTitle & "</hr></h2>")

WriteLine(ViewToHtmlTable(dv, _groupColumnName))

WriteLine("</body>")

WriteLine("</html>")

DumpToFile(filePath)

End Sub

Private Sub DumpToFile(ByVal targetFile As String)

Dim sw As StreamWriter

Try

sw = New StreamWriter(targetFile, FileMode.OpenOrCreate)

sw.Write(_sb.ToString)

sw.Close()

Finally

If Not sw Is Nothing Then

sw.Close()

End If

End Try

End Sub

Private Sub WriteLine(ByVal s As String)

Write(s)

_sb.Append(NewLine)

End Sub

Private Sub Write(ByVal s As String)

_sb.Append(s)

End Sub

Private Function KeyboardCommandTable() As DataTable

Dim dt As New DataTable

dt.Columns.Add("Category")

dt.Columns.Add("Command")

dt.Columns.Add("Scope")

dt.Columns.Add("PrimaryKey")

dt.Columns.Add("Key")

For Each cmd As EnvDTE.Command In DTE.Commands

If cmd.Bindings.Length > 0 Then

ParseBindings(cmd, dt)

End If

Next

Return dt

End Function

Private Sub ParseBindings(ByVal cmd As EnvDTE.Command, ByVal dt As DataTable)

If cmd.Name Is Nothing Then Return

If cmd.Name.Length = 0 Then Return

Debug.WriteLine(cmd.Name)

Dim dr As DataRow = dt.NewRow

Dim sa() As String = Regex.Split(cmd.Name, "\.")

dr.Item("Category") = sa(0)

If sa.Length = 1 Then

dr.Item("Command") = ""

Else

dr.Item("Command") = sa(1)

End If

For Each s As String In cmd.Bindings

ParseBinding(s, dr)

Next

dt.Rows.Add(dr)

End Sub

Private Sub ParseBinding(ByVal s As String, ByVal dr As DataRow)

Dim sa() As String = Regex.Split(s, "::")

dr.Item("Scope") = sa(0)

dr.Item("Key") = sa(1)

Dim primarykey As String = Regex.Match(sa(1), _

"(?<key>[^+]+),|(?<key>[^+]+$)").Groups("key").ToString

dr.Item("PrimaryKey") = primarykey

End Sub

Private Function PrettyKeyHtml(ByVal key As String) As String

If Not key.EndsWith("+") Then

key = Regex.Replace(key, "(\+|,\s)", "</span>$1<span class=""kbd"">")

End If

Return "<p class=""i20""><span class=""kbd"">" & key & "</span>"

End Function

Private Function ViewToHtmlTable(ByVal dv As DataView, _

ByVal groupColumnName As String) As String

Dim sb As New StringBuilder

Dim prevGroupName As String

Dim groupName As String

sb.Append("<table>")

sb.Append(NewLine)

For Each drv As DataRowView In dv

groupName = Convert.ToString(drv.Item(groupColumnName))

If groupName <> prevGroupName Then

sb.Append("<tr><td colspan=3><br/><h3>")

sb.Append(groupName)

sb.Append("<hr></h3>")

End If

sb.Append("<tr><td><code>")

sb.Append(PrettyKeyHtml(drv.Item("Key")))

sb.Append("</code><td>")

sb.Append(drv.Item("Category"))

sb.Append("<td><b>")

sb.Append(drv.Item("Command"))

sb.Append("</b>")

sb.Append(NewLine)

prevGroupName = groupName

Next

sb.Append("</table>")

sb.Append(NewLine)

Return sb.ToString

End Function

End Module

 

Renaming Cruise Control .Net Project Maintaining Logs and State Data

I just came across this little gotcha.

I needed to rename a cruise control .net build so I edited the .config file project block:

<project>

<name>Put my new name here</name>

....

</project>

Sure enough the build appeared under the new name in Web Dashboard.

CCTray had to be edited to remove the build with the old name and pick up the new - all was well until I noticed that the project page had lost it's log files.

The culprit was the xmlLogger configuration in the .config file - I had used the default setting which is a relative path which uses the project name and so was looking in the wrong place.

A quick change to point the logger at a specific path and hey presto!

<xmllogger>

<logDir>Full path to Logs directory here</logDir>

</xmllogger>

Next was the loss of the previous build state info - again simply renaming the state file fixed the issue since Cruise names the state file automatically using the project name:

e.g. oldName.state -> newName.state

Windows Vista Beta Officially Released

The beta of Windows Vista has been released for general download:

http://msdn.microsoft.com/windowsvista/downloads/products/getthebeta/default.aspx

If you are not an Msdn subscriber you will be required to register to obtain the download.