Skip to content

Cory Foy

Organizational agility through intersecting business and technology

Menu
  • FASTER Fridays
  • Mapping Mondays
  • Player Embed
  • Search Videos
  • User Dashboard
  • User Videos
  • Video Category
  • Video Form
  • Video Tag
Menu

Writing tests for private void classes

Posted on November 4, 2005 by Cory Foy

Recently, I got an email from someone who was trying to figure out how they should go about testing a private void class. They had an existing legacy app they were trying to get under test, and just weren’t sure how to approach it. The methods looked like:

private void menuItem15_Click(object sender, System.EventArgs e)
{
  Fdialog = new FontDialog();
  Fdialog.Font = rtbDisplay.Font;
  if(Fdialog.ShowDialog()!= DialogResult.Cancel )
  {
    rtbDisplay.Font = Fdialog.Font;
  }
}

which is one of the harder classes to test – an event handler that creates a UI widget, and sets some other UI widget.

So, how does one go about testing something like this? Well, first we start by asking what it is we want to test. In the above snippet, you have two seperate things going on. One, when a user clicks a certain menu item, they are asked what Font they want to change to. Two, when a user selects a Font from a specific dialog, the font of their display element should change.

My first suggestion was simply to manually test this. Click the button, choose a font from the display, and see if the display font changes. Conversely, click the button, choose cancel from the dialog, and make sure the display font doesn’t change.

And while that’s all well and good, it doesn’t answer the question of how we could test this in an automated fashion. First and foremost, we need to get this into a single-responsibilty harness so we can test the actions independently. Fairly simple, just extract method from the event handler:

private void menuItem15_Click(object sender, System.EventArgs e)
{
  rtbDisplay.Font = GetFontFromUserSelection(rtbDisplay.Font);
}

public Font GetFontFromUserSelection(Font defaultFont)
{
  Fdialog = new FontDialog();
  Fdialog.Font = defaultFont);
  if(Fdialog.ShowDialog()!= DialogResult.Cancel )
  {
    return Fdialog.Font;
  }

  return defaultFont;
}

That doesn’t really buy us a lot though, because even if we called GetFontFromUserSelection from a test, we’d have to deal with a UI element. So, the next step is to handle only having the UI element called when we want to. To do that, I’m going to inject the control that GetFont should use. To do that, I’m going to choose to have GetFont act on an interface. So, now we have:

public interface IFontDialog
{
  DialogResult ShowDialog();
  Font Font{get; set;}
}

Which we now subclass FontDialog so we can have it implement our interface:

public class MyFontDialog : FontDialog, IFontDialog
{

}

with those classes created, we can now do:

private void menuItem15_Click(object sender, System.EventArgs e)
{
  rtbDisplay.Font = GetFontFromUserSelection(rtbDisplay.Font, new MyFontDialog());
}

public Font GetFontFromUserSelection(Font defaultFont, IFontDialog fontDialog)
{
  fontDialog.Font = defaultFont);
  if(fontDialog.ShowDialog()!= DialogResult.Cancel )
  {
    return fontDialog.Font;
  }

  return defaultFont;
}

Up to this point, the app should still work exactly as it did before. Unfortunately, because we don’t have any tests in place, we can’t know, but I’d be willing to bet on it (and I’m not much of a betting man).

So, now we have a nice seam where we can pass our own FontDialog and see just what happens. So, let’s create a FontDialog we have a little more control over:

public class MockFontDialog : IFontDialog
{
  public MockFontDialog() {}

  public Font userFont;
  public Font MOCK_SELECTED_FONT = new Font("Times New Roman", 14);

  public DialogResult ShowDialog()
  {
    return DialogResult.OK;
  }

  public Font Font
  {
    get{return MOCK_SELECTED_FONT;}
    set{userFont = value;}
  }
}

Based on that, we can now write our first test:

[Test]
public void UserSelectionOfANewFontShouldReturnTimesNewRoman()
{
  Font defaultFont = new Font("Arial", 16);
  Font expectedFont = new Font("Times New Roman", 14");
  MockFontDialog fontDialog = new MockFontDialog();

  MyForm form = new MyForm();
  Assert.AreEqual(expectedFont, form.GetFontFromUserSelection(defaultFont, fontDialog));
}

And, our second, which I’m going to just call the event handler directly:

[Test]
public void UserSelectionOfANewFontShouldChangeTheDisplayFont()
{
  Font defaultFont = new Font("Arial", 16);
  Font expectedFont = new Font("Times New Roman", 14");
  MockFontDialog fontDialog = new MockFontDialog();

  MyForm form = new MyForm();
  form.menuItem15_Click(null, null);
  Assert.AreEqual(expectedFont, form.Display.Font);
}

Which fails! That’s because, even though we can inject a MockFontDialog into the GetFontFromUserSelection method, we don’t have a way of injecting it into the form.

There’s a couple of things we could do here. We could inject the FontDialog into the Form itself. Or, we could have the form ask what FontDialog it should use, say, from a Factory method:

private void menuItem15_Click(object sender, System.EventArgs e)
{
  IFontDialog fontDialog = FontDialogFactory.GetFontDialog();
  rtbDisplay.Font =
    GetFontFromUserSelection(rtbDisplay.Font, fontDialog);
}

We can then use the Factory to return the appropriate object based on whether we are testing. It’s not ideal, but such is the case when you are dealing with legacy code. I’ll use a configuration setting here to determine it:

public class FontDialogFactory
{
  public static IFontDialog GetFontDialog()
  {
    bool weAreTesting =
      bool.Parse(ConfigurationSettings.AppSettings["testing"]);

    if(weAreTesting)
    {
      return new MockFontDialog();
    }
    else
    {
      return new MyFontDialog();
    }
  }
}

And there you have it. A way to test what is going on with UI elements using a sprinkling of mocks and a touch of dependency injection. Not pretty, but if you need to get your UI elements under test, this might be one option for you. Ideally your business and presentation logic would actually delegate to a presenter which you could then mock much easier (known as the Model-View Presenter pattern).

So, in summary. To test an “untestable” method, y
ou have to inject some sort of a seam. Whether it be a variable inside whose results you can test, a side-effect you can check, or extracting a method and injecting mocks, getting a seam in the method is the most vital part to getting it under test.

6 thoughts on “Writing tests for private void classes”

  1. Scott Bellware says:
    November 4, 2005 at 1:08 pm

    Great post! One thing though…

    If you’re going to add a configuration item to influence which IFontDialog that the FontDialogFactory will create, why not just put the class name of the font dialog in the config and then instantiate it using the System.Reflection.Actovator? This will free your code from external environmental conditions, and will know couple Cyclomatic Complexity points off of the GetFontDialog method.

    And… if you add the class name to configuration, you might as well just use Spring to activate the instance using it’s XmlObjectFactory or ApplicationContext so that you don’t have to write the activation code yourself :)

  2. Scott Bellware says:
    November 4, 2005 at 1:12 pm

    Doh! That should have read “…knock a couple Cyclomatic Complexity points off of the…”

  3. Carl says:
    November 5, 2005 at 11:21 am

    I think the revision has changed behavior. In the original, the current font was unaffected if the user clicked cancel; in the revision, it is set to the default. So I’d write a test where the current font was not default and cancel gets “clicked”.

  4. Anonymous says:
    November 5, 2005 at 11:40 am

    Cory…don’t know if this’ll make it back to you or not, sure hope so. Please tell Crissie that I thoroughly enjoyed the tour of your photo gallery – “baby”, wedding pics, and especially Gail’s awesome “jump”! Glad you all are doing so great! Good luck w/the pregnancy and baby preparations. Give “Mommy” a big hug from a very looooong time old friend and fan! Cheers…BJ

  5. Anthony Williams says:
    November 7, 2005 at 6:15 am

    I think that the GetFontFromUserSeletion function belongs on a separate class. I have added an entry to my blog giving more detail at: http://www.livejournal.com/users/anthony_w/8000.html

  6. Cory Foy says:
    November 8, 2005 at 2:08 am

    “I think the revision has changed behavior. In the original, the current font was unaffected if the user clicked cancel; in the revision, it is set to the default. So I’d write a test where the current font was not default and cancel gets “clicked”.”

    Hi Carl,

    You do have a point, although the Font technically wouldn’t change, since the default font is passed in, so if the user hits cancel it would just get set to what it was before.

    This could cause events to fire, like OnFontChange events. So perhaps extracting the method wasn’t the best way to preserve behavior.

    Thanks for pointing it out!

Comments are closed.

© 2025 Cory Foy | Powered by Superbs Personal Blog theme