2012-10-15 31 views
0

我想实现一些单元测试到遗留的PHP应用程序。单元测试遗留的PHP类与沉重的数据库依赖

已经有许多与此挑战,但特别是对于这个问题,我目前正在在管理应用程序配置一个小班。

该类的界面非常简单;它执行以下操作:

  • 构造函数调用Populate方法,该方法使用我们的Recordset类从数据库加载所请求模块的配置。
  • Get方法,返回指定的配置值。
  • 设置方法,它将配置值设置在内存中。
  • 保存方法,它将配置更新写回数据库。

测试get/set方法很简单;他们直接映射到私有数组,并且可以像您期望的那样工作。

我遇到的问题是测试数据库处理。该类使用配置表(模块名称,语言等)上的许多字段来确定要加载的配置项以及优先级。为此,它构建了一系列精心设计的SQL字符串,然后直接调用数据库以获取正确的配置数据。

我不知道如何去写一个单元测试此。除了Get/Set方法之外,该类几乎完全包含构建SQL字符串并运行它们。

我看不出与要走一段路理智测试不实际运行它反对真正的数据库,所有的问题 - 如果不出意外,在配置装载机的复杂性将意味着我需要至少有七个或八个测试数据库填充了稍微不同的配置。看起来这将是难以管理和脆弱的,这将有点失败。

任何人都可以建议我应该从这里出发?是否可以单元测试这种类?

非常感谢。

回答

1

我必须说,我不知道我同意,单元测试会有些无意义这里没有命中数据库。我的目标是获得测试中生成SQL的业务逻辑,而不涉及数据库。下面是我在说什么的例子:

class Foo { 

    // ... Getters and setters for your config ... 

    public function doSomeBusinessLogicThenHitDb() 
    { 
     $sql = 'SELECT * FROM mytable WHERE '; 
     $sql .= $this->_doSomethingComplicatedThatInvolvesParsingTheConfig(); 
     $this->_queryDb($sql); 
    } 

    protected function _queryDb($sql) 
    { 
     // Do something with a PDO or whatever 
    } 
} 

已经抽象的_queryDb()位到一个单独的功能,你就可以写这个测试:

public function testMyClassUnderSomeCircumstances() 
    { 
     // Set up config 
     $exampleConfig = // whatever 

     // Set up expected result 
     $whatTheSqlShouldLookLikeForThisConfig = 'SELECT ... WHERE ...'; 

     // Set up a partial mock that doesn't actually hit the DB 
     $myPartialMockObject = $this->getMock('Foo', array('_queryDb'), array(), ''); 
     $myPartialMockObject->expects($this->once()) 
          ->method('_queryDb') 
          ->with($whatTheSqlShouldLookLikeForThisConfig); 

     // Exercise the class under test 
     $myPartialMockObject->setConfig($exampleConfig); 
     $myPartialMockObject->doSomeBusinessLogicThenHitTheDb(); 
    } 

本次测试的重点是测试产生的业务逻辑你的SQL - 不要测试你的数据库本身。通过预期产生的SQL必须看起来像它看起来必须的样子,你确保你的测试会失败,如果无知的重构_doSomethingComplicatedThatInvolvesParsingTheConfig()意外地破坏你的代码,使它产生与过去不同的SQL。

如果测试整个应用程序(包括其数据库)是您的目标,请尝试像Selenium这样的适当集成测试套件。单元测试可以监控各个班级,并告诉你他们什么时候停止了他们应有的行为。你会面临执行速度和错误本地化的问题(即使是在代码中的错误,更不用说被测试的类,或者它是数据库的东西?),如果你让他们超越。

+1

感谢您的回答。意外考虑到问题的年龄。:-)自从发布这个问题以来,我最终做了一个相当大规模的重构练习,主要是为了让所有代码进入可测试状态。那是前一阵子,而且一切都很顺利。但是你给出的建议很好,我会给你+1并接受答案。感谢您为帮助而努力。 – SDC

0

为了更好地测试这些东西,一个直接的方法就是给你的配置类一个访问数据库的对象,这样你就可以用真实数据库运行任何配置,或者只是模拟一些写入内存的配置,甚至轻量级文件例如,如果你需要持久性。

这可以通过创建具有定义的接口的适配器来完成。您编写的第一个适配器适用于您的数据库。

当配置对象被创建时,你传入适配器。由于它具有定义的接口,所以config类可以与具有该接口的任何适配器一起使用。首先是数据库。

然后您可以模拟一个适配器或写它自己的适配器为你的测试。在测试中,您不使用数据库适配器,而是使用测试适配器。

然后,您可以单独测试独立于数据库的配置类。

+0

感谢您的回复。我想这个问题的一部分是,类构建的查询真的是功能的地方。嘲笑数据库意味着我们不运行查询。在不运行查询的情况下,测试该类变得更容易,但也没有多大意义。查询使用MySQL方言,因此可能无法使用sqllite表,所以这也不起作用。 :( – SDC