Dean Friedland
Dean Friedland

Reputation: 803

Nunit: Testing legacy code (private void) method

I have been tasked with building unit tests for a bunch of legacy code. The specific task/goal for the below method is to test that the messageProcessor.ProcessCustomerPhoneContactInfo(currentPhoneContact) method is being called. I am also posting the test stub I have written so far but I would appreciate some direction because I think I am going down a rabbit hole here. How can I fill in the blanks on my test?

Method being tested:

private void logPhoneCallDialog_SaveContact(Contact currentPhoneContact)
    {           
        if (currentPhoneContact != null)
        {
            RefreshRenewalActivity();

            if (currentPhoneContact.TypeId == ResultType.TookAppointment)
        }

        NotifyServerOfActivity();

        ApplyAppointmentFilters();

        this.Activate();

        var messageProcessor = new MessageProcessor();
        messageProcessor.ProcessCustomerPhoneContactInfo(currentPhoneContact);
    }

Test:

[TestFixture, RequiresSTA]
class BucketBrowserTest
{
    [Test]
    public void logPhoneCallDialog_SaveContact()
    {
        //Arrange            

        //Act

        //Assert
    }
}

Method that calls above method

private void ShowPhoneCallLoggerDialog()
    {
        PhoneCallLoggerDialog dialog = new PhoneCallLoggerDialog(CurrentCustomer, CurrentBucket.BucketTypeId);
        dialog.Owner = this;
        dialog.SaveContact += new PhoneCallLoggerDialog.SaveContactHandler(logPhoneCallDialog_SaveContact);

        dialog.ShowDialog();
    }

Event Handler for calling method

public delegate void SaveContactHandler(PhoneContact currentPhoneContact);
    public event SaveContactHandler SaveContact;

Upvotes: 0

Views: 211

Answers (2)

John H
John H

Reputation: 14640

Based on the additional information you've supplied, I'm going to outline my assumptions before describing a possible solution:

  1. You're able to safely construct an instance of this class, without calling anything out of process
  2. Calling logPhoneCallDialog_SaveContact(), won't trigger side effects that prevent it from being tested

When refactoring legacy code, you often have to make design choices that you would normally avoid. This can include:

  1. Testing implementation details
  2. Making methods public or internal
  3. Adding light abstractions that simply facilitate testing

In order to get a test around this, you're going to have to do at least one of those things.

Firstly, make logPhoneCallDialog_SaveContact public:

public void logPhoneCallDialog_SaveContact(Contact currentPhoneContact)
{
    // same body as before
}

Next, extract a method that holds the entire body of the first one, to end up with this:

public void logPhoneCallDialog_SaveContact(Contact currentPhoneContact)
{
    SaveContact(currentPhoneContact);
}

private void SaveContact(Contact currentPhoneContact)
{
    if (currentPhoneContact != null)
    {
        RefreshRenewalActivity();

        // This code from your example doesn't compile.
        if (currentPhoneContact.TypeId == ResultType.TookAppointment)
    }

    NotifyServerOfActivity();

    ApplyAppointmentFilters();

    this.Activate();

    var messageProcessor = new MessageProcessor();
    messageProcessor.ProcessCustomerPhoneContactInfo(currentPhoneContact);
}

Make the new method public:

public void SaveContact(Contact currentPhoneContact)
{
    // same body as before
}

If you haven't already, extract an interface for MessageProcessor:

public interface IMessageProcessor
{
    ProcessCustomerPhoneContactInfo(Contact currentPhoneContact);
}

public class MessageProcessor : IMessageProcessor
{
    public void ProcessCustomerPhoneContactInfo(Contact currentPhoneContact)
    {
        // implementation
    }
}

Now modify the methods like so:

public void logPhoneCallDialog_SaveContact(Contact currentPhoneContact)
{
    var messageProcessor = new MessageProcessor();
    SaveContact(currentPhoneContact, messageProcessor);
}

public void SaveContact(
    Contact currentPhoneContact,
    IMessageProcessor messageProcessor)
{
    if (currentPhoneContact != null)
    {
        RefreshRenewalActivity();

        if (currentPhoneContact.TypeId == ResultType.TookAppointment)
    }

    NotifyServerOfActivity();

    ApplyAppointmentFilters();

    this.Activate();

    messageProcessor.ProcessCustomerPhoneContactInfo(currentPhoneContact);
}

Now write your unit tests against SaveContact, mocking IMessageProcessor, instead of against logPhoneCallDialog_SaveContact.

Edit

Here's an example, as requested. It's been a while since I've used Moq - which was in your original question - so the syntax may not be quite right, but something like this:

[Test]
public void SavesContact()
{
    // Arrange
    var contact = new Contact();
    var messageProcessor = new Mock<IMessageProcessor>();

    var subject = // whatever class contains the logPhoneCallDialog_SaveContact method          

    // Act
    subject.SaveContact(contact, messageProcessor.Object);

    // Assert
    messageProcessor.Verify(x => x.ProcessCustomerPhoneContactInfo(contact), Times.Once());
}

Also test the case where contact is null.

Upvotes: 1

Neil
Neil

Reputation: 11909

With the code as it stands, you cannot mock out the messageProcessor, but with a few changes, you could:

IMessageProcessorFactory _messageProcessorFactory;

public TheConstructor(IMessageProcessorFactory processorFactory)
{
    _messageProcessorFactory = processorFactory;
}

private void logPhoneCallDialog_SaveContact(Contact currentPhoneContact)
{           
    if (currentPhoneContact != null)
    {
        RefreshRenewalActivity();

        if (currentPhoneContact.TypeId == ResultType.TookAppointment)
    }

    NotifyServerOfActivity();

    ApplyAppointmentFilters();

    this.Activate();

    var messageProcessor = _messageProcessorFactory.Create();
    messageProcessor.ProcessCustomerPhoneContactInfo(currentPhoneContact);
}

Then you can Moq/Mock the interface and find out if the function was called.

Upvotes: 0

Related Questions