2016-02-04 64 views
1

我有一个包含规则的二进制文件。我将这些规则解析为对象并对它们扫描文件。我现在的对象模型,像这样:如何让这些类更加单元测试友好?

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; 
}; 

我明白依赖注入将与测试Client类(通过传递引用嘲笑RuleParserRuleScanner对象,它的构造函数)的帮助。但是,我需要做些什么来单元测试RuleParserRuleScanner类?因为RuleDatabase是用于存储其他对象的哑对象,所以在当前模型中依赖注入不起作用。我最初的想法是修改RuleDatabase以隐藏其数据成员并提供公开的方法来对其进行操作,例如ParseTypeA(),ParseTypeB(),ScanTypeA(),ScanTypeB()。但是,我认为这似乎是模糊了班级职责之间的界限(例如,RuleParser应该执行所有解析工作,而RuleScanner应该执行所有扫描工作)。有没有更干净的方法来做到这一点?

+0

目前,你可以单独测试'Parser','Scanner'没有'Parser'(你必须自己创建'RuleDatabase')。 “客户”大多只是转发它的任务,所以测试它主要是集成测试。 – Jarod42

+0

这真的取决于你想测试什么。如果它只是一个简单的测试,比如'TestScanFileReturnsTrueWhenDatabaseContainsX',那么你可以很容易地预先填充一个'RuleDatabase'对象,并将它传递给你的'ScanFile'方法。当测试应该模拟对象时,并非所有传递给方法的东西 –

+0

*“如何让这些类更易于使用单元测试?” - 单元测试不应损害类架构! –

回答

1

使用Inversion of Control principle(通常称为IoC),这样你就可以独立地从一个到另一个测试你的类。

但是,不要陷入极端,保持简单的类,就像使用标量类型一样,只是单元测试它们。

就你而言,我会将你的RuleParserRuleScanner课程从你的Client课程中外化。

您的RuleScanner类也可以通过插入一个接口来实现,您也可以通过外部化规则列表,因此您将能够单独测试每个规则。

+0

这实际上是我最终做的,所以我接受了答案。 – Luke

1

将一个istream传递给ParseFile方法。这样,您可以在测试文件中创建一个流,作为istringstream,而不需要为测试提供文件。

使用TDD通过测试帮助驱动课程设计可能会更好。设计一个测试类比测试一个已经存在的设计要容易得多。