直接访问DBI有什么问题?

时间:2011-01-12 11:10:03

标签: perl unit-testing coding-style dbi

我正在阅读Effective Perl Programming(第2版)。我遇到过一段被​​描述为写得不好的代码,但我还不了解它的内容是什么,或者它应该如何改进。如果有人能向我解释这件事,那就太好了。

以下是相关代码:

sub sum_values_per_key {
   my ( $class, $dsn, $user, $password, $parameters ) = @_;
   my %results;

   my $dbh = 
     DBI->connect( $dsn, $user, $password, $parameters );

   my $sth = $dbh->prepare(
     'select key, calculate(value) from my_table');
   $sth->execute();

   # ... fill %results ...

   $sth->finish();
   $dbh->disconnect();

   return \%results;
}

示例来自关于测试代码的章节(第324/325页)。让我想知道如何改进代码的句子如下:

  

由于代码编写不好并直接访问DBI,因此您必须创建一个假的DBI对象来代替真实的东西。

我可能不太了解这本书迄今为止试图教给我的很多内容,或者我已经跳过相关部分来理解上述代码的不良做法...好了,先谢谢你的帮助!

5 个答案:

答案 0 :(得分:9)

由于本章是关于测试的,请考虑这一点:

在测试您的功能时,您也(隐式)测试DBI。这就是为什么它很糟糕。

良好的测试始终只检查一项功能。为了保证这一点,它将是必需的 不直接使用DBI,而是使用模拟对象。这样,如果您的测试失败,那么您 知道这是你的功能,而不是另一个模块中的其他东西(比如你的例子中的DBI)。

答案 1 :(得分:6)

我认为布莱恩试图通过“写得不好”说的是,你没有业务逻辑和数据访问代码(以及数据库连接机制,而在它)之间的分离。

编写函数的正确方法是函数(或方法)应该一件事,而不是一次完成三件事。

由于这一大块功能,测试时,你必须同时测试所有这三个,这很困难(参见那些段落中使用“测试SQLite DB”的讨论) )。或者,作为替代方案,执行本章的内容,并通过假装数据访问和数据库设置以某种方式工作来模拟DBI对象以测试业务逻辑。

但是,像DBI一样嘲弄复杂行为的对象是非常非常复杂的。

如果无法访问数据库怎么办?如果有阻塞怎么办?如果您的查询有语法错误怎么办?如果执行查询时数据库连接超时怎么办?如果......

良好的测试代码测试所有这些错误情况等。


代码的更正确的方法(模式)是:

my $dbh = set_up_dbh();
my $query = qq[select key, calculate(value) from my_table];
my $data = retrieve_data($dbh, $query);
    # Now, we don't need to test setting up database connection AND data retrieval
my $calc_results = calculate_results($data);

这样,要测试calculate_results中的逻辑(例如汇总数据),你只需要模拟传递给它的DATA,这很容易(在很多情况下,你只需要在一些测试配置中存储几组测试数据) );而不是模拟用于检索数据的复杂DBI对象的行为。

答案 2 :(得分:5)

单独使用DBI没有任何问题。

线索在于这是测试章节。我假设指出的问题是该函数打开并关闭数据库连接本身。它应该期望数据库句柄作为参数,并且只对其运行查询,而不关心打开和关闭与其调用者的数据库连接。这将使功能的工作更加狭窄,因此它使功能更加灵活。

这反过来又使函数更容易测试:只需将模拟对象作为数据库句柄传递给它。正如目前所写,你至少需要重新定义DBI::connect来测试它,这并不难,但肯定是凌乱的。

答案 3 :(得分:5)

一个名为sum_values_per_key的方法应该感兴趣的是对某些键的值求和,而不是获取要求和的数据。

它不符合SOLID编程的S(单一责任原则)。 http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29

这意味着它们都是:

  • 如果您希望使用不同的源数据,则无法重复使用。
  • 在没有数据库连接的环境中难以测试。

答案 4 :(得分:2)

1)假设你有十几个对象,每个对象都有十几个这样的方法。在执行主程序期间将调用其中20个方法。您现在已经建立了20个DB连接,只需要一个。

2)假设您对原始DBI不满意并使用My :: DBI扩展它。您现在必须在12个文件中重写144个函数。

(Apache :: DBI可能就是一个例子)。

3)每次调用144个函数时,必须携带3个位置参数。人脑一次可以很好地处理大约7个物体;你差不多只有一半的空间。这使得代码不易维护。