Reputation: 11421
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
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
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