Kobojunkie
Kobojunkie

Reputation: 6535

Unit Testing and Access modifiers in OO

I notice that in order to unit test each unit in my OO code, I need to set the access modifiers to public, even on methods that ought to be protected, or probably private. Is this OK practice?

public class EnforceBusinessRules
{
    BusinessState m_state;

    public EnforceBusinessRules()
    {
        m_state = START;
    }


    public bool isInputcurrentlyFormatted(string input)
    {
        //code goes here to ensure the input passes formatting test
        //modify m_state appropriately
    }


    public bool InputContainsValidStartAndEndTokens(string input)
    {
        //code goes here to ensure that the start and end tokens of the input are of the type available in the system
        //modify m_state appropriately
    }


    public bool StartEndCommandisValidAccordingtoCurrentSystemSettings(string input)
    {
        //code goes here to check the start and End codes match the current start and end codes for the day
        //modify m_state appropriately
    }

    // and so on 
}

Upvotes: 3

Views: 2759

Answers (6)

Morten
Morten

Reputation: 3844

If you need to test private or protected properties or methods, then you should extract the functionality into separate classes. Each class should only do one thing (Single Responsibility Principle). Odds are that your private methods perform the secondary functionality on the main purpose of your class.

When extracted, two good things happen: First, the code you wish to test is now fully accessible by unit tests. Second, you don't have to worry about it in your EnforceBusinessRules class.

Upvotes: 0

bwalk2895
bwalk2895

Reputation: 564

Typically it is bad practice to expose methods/classes/properties/whatever to the outside world you don't want to expose. We ran into a similar issue where I work. We wrote tests that hit the public methods and properties, because those public methods should, at some point, invoke all the private methods. In the case where you have internal classes/properties/methods you can use the InternalsVisableTo attribute to them accessible to your unit testing library.

[assembly: InternalsVisibleTo("AssemblyB")]

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

Upvotes: 0

Josh Peterson
Josh Peterson

Reputation: 2329

If you find that you need to change access modifiers because you have plenty of code in private members that you need to test, it may be a sign that your class is too large. Consider breaking the behavior you want to test into smaller classes with public interfaces that you can test. Your code may be suffering from the god object code smell.

Remember that protected members are very similar to public members, except that the space of possible clients is much smaller (derived classes only). You could consider unit testing those methods by creating a derived object which only your test uses. In this way, you will be testing these methods in the same manner your clients will use them.

Here is one way that you might modify your code:

public class EnforceBusinessRules
{
    BusinessState m_state;

    public EnforceBusinessRules(IEnumerable<Rule> rules)
    {
        m_state = START;
    }

    public void Enforce(string input)
    {
        foreach (var rule in rules)
        {
            m_state = rule.EnforceRule(input);
        }
    }
}

public interface Rule
{
    public BusinessState EnforceRule(string input);
}

public class IsInputcurrentlyFormatted : Rule
{
    public BusinessState EnforceRule(string input)
    {
        //code goes here to ensure the input passes formatting test
    }
}

public class InputContainsValidStartAndEndTokens : Rule
{
    public BusinessState EnforceRule(string input)
    {
        //code goes here to ensure the input passes formatting test
    }
}

public class StartEndCommandisValidAccordingtoCurrentSystemSettings : Rule
{
    public BusinessState EnforceRule(string input)
    {
        //code goes here to ensure the input passes formatting test
    }
}

// and so on

Upvotes: 1

Rob Kielty
Rob Kielty

Reputation: 8152

It sounds like you are failing to create getter and setter methods so that your private data is correctly encapsulated.

The unit tests should access data via getters and setters.

Avoid instantiating collaborating objects in your class code, inject those dependencies instead.

Upvotes: 0

Preet Sangha
Preet Sangha

Reputation: 65466

No. You are thinking about it wrongly in my opinion. You can infer things without making things public. A property's value could be realised from a function return for example.

In the case of a protected member. Ask your self what that means. It means that derived classes can access that member. So make a derived type in the test.

Upvotes: 0

Liam
Liam

Reputation: 29634

Unit testing is "Black box" testing. You should only test the externally visible elements. If you test all the internal workings then you can't refactor your code correctly without modifying all your unit tests.

Upvotes: 8

Related Questions