0

我正在尝试在遗留 PHP 应用程序中实现一些单元测试。

这方面存在许多挑战,但特别是对于这个问题,我目前正在研究一个管理应用程序配置的小类。

类接口非常简单;它执行以下操作:

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

测试 Get/Set 方法很简单;它们直接映射到私有数组,并且几乎可以按照您的预期工作。

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

我不知道如何为此编写单元测试。除了 Get/Set 方法之外,该类几乎完全由构建 SQL 字符串和运行它们组成。

如果没有针对真实数据库实际运行它,我看不到一种明智地测试它的方法,以及随之而来的所有问题——如果不出意外的话,配置加载器的复杂性意味着我至少需要七个或八个测试数据库填充了稍微不同的配置。看起来它会变得难以管理和脆弱,这会在某种程度上破坏这一点。

谁能建议我应该如何从这里开始?甚至可以对这种类进行单元测试吗?

非常感谢。

4

2 回答 2

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。单元测试监视各个类并告诉您它们何时停止按应有的行为。如果您让它们超出范围,您将面临执行速度和错误本地化的问题(即错误甚至在代码中,更不用说被测试的类,还是数据库的东西?)。

于 2013-01-27T21:24:02.647 回答
0

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

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

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

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

然后,您可以独立于数据库对配置类进行单元测试。

于 2012-10-15T13:16:55.343 回答