Luke
Luke

Reputation: 11421

How do I make these classes more unit-test friendly?

I have a binary file containing rules. I parse these rules into objects and scan files against them. My current object model is like so:

struct TypeARule
{
    // rule internals
};

struct TypeBRule
{
    // rule internals
};

struct TypeCRule
{
    // rule internals
};

struct RuleDatabase
{
    vector<TypeARule> TypeA;
    vector<TypeBRule> TypeB;
    vector<TypeCRule> TypeC;
};

class RuleParser
{
public:
    bool ParseFile(const string& path, RuleDatabase& database)
    {
        // open file, read records from file, generate objects from records, add objects to database
    }
private:
    // misc helper methods
};

class RuleScanner
{
public:
    bool ScanFile(const string& path, const RuleDatabase& database)
    {
        if(ScanTypeA(path, database))
        {
            return true;
        }
        if(ScanTypeB(path, database))
        {
            return true;
        }
        if(ScanTypeC(path, database))
        {
            return true;
        }
        return false;
    }
private:
    bool ScanTypeA(const string& path, const RuleDatabase& database)
    {
        for(const auto& rule : database.TypeA)
        {
            if (rule matches path))
            {
                return true;
            }
        }
        return false;
    }
    bool ScanTypeB(const string& path, const RuleDatabase& database)
    {
        for(const auto& rule : database.TypeB)
        {
            if (rule matches path))
            {
                return true;
            }
        }
        return false;
    }
    bool ScanTypeC(const string& path, const RuleDatabase& database)
    {
        for(const auto& rule : database.TypeA)
        {
            if (rule matches path))
            {
                return true;
            }
        }
        return false;
    }
};

class Client
{
public:
    bool Initialize(const string& path)
    {
        RuleParser parser;
        return parser.ParseFile(path, m_database);
    }
    bool ScanFile(const string& path)
    {
        RuleScanner scanner;
        return scanner.ScanFile(path, m_database);
    }
    void Cleanup()
    {
        // cleanup m_database
    }
private:
    RuleDatabase m_database;
};

I understand dependency injection would help with testing the Client class (by passing references to mock RuleParser and RuleScanner objects to its constructor). But what would I need to do to be able to unit test the RuleParser and RuleScanner classes? Dependency injection won't work in the current model since RuleDatabase is a dumb object used to store other objects. My initial thought is to modify RuleDatabase to hide its data members and provide public methods to operate on them, such as ParseTypeA(), ParseTypeB(), ScanTypeA(), ScanTypeB(). However, that seems to me to be blurring the lines between class responsibilities (e.g. RuleParser should do all the parsing work and RuleScanner should do all the scanning work). Is there a cleaner way to do this?

Upvotes: 1

Views: 189

Answers (2)

dkg
dkg

Reputation: 1775

Use the Inversion of Control principle (often referred as IoC), that way you will be able to test your classes independently from one to another.

But do not fall in the extreme, keep your simple classes as you would do with scalar types, just unit test them.

In your case, I would externalize your RuleParser and your RuleScanner class from your Client class.

Your RuleScanner class could also take a list of rules that you could externalize too by inserting an interface to implement, hence you will be able to test each rule separately.

Upvotes: 1

graham.reeds
graham.reeds

Reputation: 16476

Pass an istream to the ParseFile method. That way you can create a stream in your test file as an istringstream without needing a file to be present for your tests.

You might be better off using TDD to help drive the design of your classes through tests. It's always easier to design a class for testing than to test an already existing design.

Upvotes: 1

Related Questions