Friday, October 19, 2007

Mocking frameworks are growing more and more in popularity these days, because to some degree Unit Testing, via TDD or otherwise, has been growing in popularity.  This week at the Heartland Developer Conference I gave a talk on what I call "practical" TDD.  The talk goes over the basics of TDD quickly, but is really targeted at those who have tried to do TDD but found it difficult because they are not working on a team that has adopted the practice, or they are not working a project that was built to be testable.  I spent a good bit of time working no what is the easiest path to help such people adopt TDD, because adoption of such good practices is far more important to me than perfection in them.  As has been said many times, Good Enough is by definition, Good Enough.

After a good bit of research on the subject of mocking frameworks, I have come to the simple conclusion that:

  1. This is an area that is growing still, as nearly every major framework differs on the coding approach.  This is in stark contrast to testing frameworks which, to a one in .NET, all have settled on the NUnit 2.0 model of using attributes.
  2. That if you're not using TypeMock then you're just working to damned hard.

Now, I'm sure my friends (and there are many) who use Rhino Mocks will believe that I must be over-stating the issue, but I tell you clearly I am not.  TypeMock is not built like any other mocking framework currently available, it uses the profiling APIs and not polymorphism or encapsulation in order to intercept calls and provide return values.  Let me give you just a few examples of things which TypeMock can do in a few short lines of code which Rhino Mocks simply cannot do at all.

Mocking Static Methods

Take the following code, and assume that we wish to mock MessageBox.Show which is a static method:

        private void MyCoolMethod(string msg)
        {
            if (MessageBox.Show(msg) == DialogResult.OK)
                Console.WriteLine("OK");
            else
                Console.WriteLine("Not OK! Not OK!");
        }

The following test will work perfectly to mock this call.  No other hidden setup, nothing more than a reference to TypeMock.dll and the following code:

        [Test]
        public void MockMessageBoxShow()
        {
            MockManager.Init();

            Mock mbMock = MockManager.Mock(typeof(MessageBox));
            mbMock.ExpectAndReturn("Show", DialogResult.OK);

            MyCoolMethod("Here we go again.");

            // Ensure that all expectations were met.
            MockManager.Verify();
        }

And with just that little code, just 4 lines dedicated to the mock, 2 of which should be refactored to Setup and TearDown methods, we can mock a static method.

Not cool enough for you? Ok, fine.

Mocking Events

So you have something which expects an object to return certain events.  This example does require a professional license of TypeMock, it will not work under the Community Edition, but if you need this functionality then really pay the nice folks their money.

    public class GUI
    {
        public string LovingCSharp { get; set; }

        public void Initialize()
        {
            this.LovingCSharp = string.Empty;
            Button button = new Button();
            button.Click += new EventHandler(button_Click);
        }

        private void button_Click(object sender, EventArgs e)
        {
            this.LovingCSharp += "LOVE!";
        }
    }

Now let's mock this up, call that event three times, and assert that our property is set correctly. 

        [Test]
        public void MockFormWithEvents()
        {
            MockManager.Init();

            // Mock button so that we can...
            Mock btnMock = MockManager.MockAll(typeof(Button));

            // Handle all calls to add an event handler.
            MockedEvent evntMock = btnMock.ExpectAddEventAlways("Click");

            GUI frm = new GUI();
            frm.Initialize();
            evntMock.Fire(this, EventArgs.Empty);
            evntMock.Fire(this, EventArgs.Empty);
            evntMock.Fire(this, EventArgs.Empty);

            Assert.AreEqual("LOVE!LOVE!LOVE!", frm.LovingCSharp);

            // Ensure that all expectations were met.
            MockManager.Verify();
        }
    }

Summary

These are just two examples, and don't even delve into the whole "Natural Mocks" portion of TypeMock.  Do yourself a favor, download the evaluation, they'll give you 30 days of all the features (which you can make any individual 30 days you'd like BTW) and ask yourself why you're jumping through all those hoops just to  be able to mock dependencies.  With this project, you don't have to create dependency injection constructors just to make your classes testable.

Friday, October 19, 2007 10:25:41 PM (Central Standard Time, UTC-06:00)
 Thursday, September 28, 2006

This TLA (Three Letter Acronym) littered post is about how to add PDB (Program DataBase) files to the GAC (Global Assembly Cache). 

Today my current client had the need to get the true source of an exception.  They had a catch statement which logged the exception (via log4net) to a file, but the location in question was a poorly designed component with hundreds of lines of code all wrapped within a very large Try-Catch which logged errors.  The error was likewise unhelpful as to the source because it was an array out of bounds exception, and this code did alot of string array manipulation.  The solution was clear to me, we needed to load the PDB files onto the server so that when an exception was thrown there would be line number information to go with it. 

The problem?  The assembly was being called by BizTalk Server and as such it was strongly named and in the GAC.

The solution?  Add the PDB files to the GAC, of course.

 The Global Assembly Cache which is located at %WINDIR%\assembly ("c:\windows\assembly" on most boxes) is in fact organized into many different directories all of which exist under %WINDIR%\assembly but which by default are hidden from view by Explorer.  Fortunately, the command prompt does not suffer the same problems.  If you open a command prompt and "cd %windir%\assembly" and do a "dir" you will see several directories as you can see in the first image.

If you change directory to "GAC" you will see a directory for every single assembly in the GAC.  From here, cd to the directory for the assembly you want, in my example nunit.framework, and you will see another interesting set of directories.  Here you will see one directory for every combination of version number and public key in the GAC.  You need to know what version you are working with, and the public key for that.  cd into that directory and you will find a copy of your assembly.  Now all you have to do is copy your PDB file to this location and your next exception should contain line numbers for the offending code. 

Since the work to generate line numbers is only done when an exception is being thrown there is little to no reason not to include the PDB files in all your production deployments to the GAC.  Yes, generating them takes time, but if you're throwing an exception this shouldn't be a problem.  After all, you are treating exceptions as exceptional, right?

.NET | ASP.NET | CSharp | VB | BizTalk Server
Thursday, September 28, 2006 8:18:45 PM (Central Standard Time, UTC-06:00)
 Saturday, August 26, 2006

Tulsa-TechFest_2006.jpgLots of stuff has been happening lately, but I am long overdue in announcing that I will be speaking at the Tulsa Tech Fest on October 14th, 2006. This looks to be a fantastic event with the very best from around the US attending, including Carl Franklin from .NET Rocks, Ron Jacobs from Microsoft's Patterns and Practices Group, and many others.

Sogeti will be there in force, with Ed Kissinger and Ed Blankenship of EdSquared.com presenting two sessions on Team Foundation Server and VSTS.  I will be presenting two talks, one on Zero Cost .NET, a demonstration of how Microsoft's products can realistically compete with the LAMP offering, and a second presentation on NUnit Extensibility.

I'm particularly excited about the Zero Cost .NET presentation, which I've been working on gathering the resources for and which should have a block-buster closer demonstration.  I hope you can find time to come out and check out what Tulsa Tech Fest has to offer.

 

 

 

 

Saturday, August 26, 2006 12:50:49 AM (Central Standard Time, UTC-06:00)
 Saturday, July 29, 2006
One my latest project working for Citigroup, I've been working with an Open Source component named PdfSharp. This wonderful little component lets you create and modify Pdf documents and has the advantage of behaving much more like a .NET component than the popular ITextSharp, which is a port of a Java PDF library.

One of the things I needed to do was put a GIF image into the bottom half of a PDF document. Simple enough using PdfSharp, just get a set of PdfDocument, XGraphic and XImage objects and do some math for the placement. All was happiness and light until we decided we needed to clean-up the created Pdf after we were done with it. Suddenly we would sometimes get Sharing Violations on deleting the file. But only sometimes.

Fortunately experience has taught me that "sometimes" errors can only be a few types of situations, and in this case one made the most sense ... Garbage Collection. I went pouring over every piece of code looking for where I missed an object that, I hoped, implemented IDisposable and sure enough discovered that PdfDocument, XGraphic and XImage all implemented IDisposable but did not have a public Dispose method. This is perfectly fine, but does make it harder to know when you should use IDisposable.

In the end, the error was of course mine. As such I have done 50 "Hail Using"s (in VB since they now support it) and lit a candle in rememberance of lost productivity.

CSharp | VB
Saturday, July 29, 2006 9:35:17 AM (Central Standard Time, UTC-06:00)
 Monday, June 27, 2005
Cory has scheduled yet another DFW Geek Gathering!  You can bet that I'll be there.
VB | XBox
Monday, June 27, 2005 2:44:00 PM (Central Standard Time, UTC-06:00)
 Tuesday, March 29, 2005
Cory Smith is putting together a Geek Gathering at the Main Event in Grapevine. I agree with Cory that User Groups and other meetings are great, but not a good place to socialize. So you can count me in!
VB
Tuesday, March 29, 2005 8:36:00 PM (Central Standard Time, UTC-06:00)