can*_*nin 6 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;
}
Run Code Online (Sandbox Code Playgroud)
该示例来自测试代码的章节(第324/325页).让我想知道如何改进代码的句子如下:
由于代码写得很差并直接访问DBI,因此您必须创建一个假的DBI对象来代替真实的东西.
我可能还没有理解这本书迄今为止试图教给我的很多东西,或者我已经跳过了相关部分以了解上述代码的不良做法......好了,先谢谢你的帮助!
由于本章是关于测试的,请考虑以下事项:
在测试函数时,您也(隐式)测试DBI.这就是为什么它很糟糕.
良好的测试总是只检查一个功能.为了保证这一点,需要不直接使用DBI,而是使用模拟对象.这样,如果您的测试失败,您知道它是您的功能而不是其他模块中的其他内容(例如您的示例中的DBI).
我认为布莱恩试图通过"写得不好"来表达的是,你没有业务逻辑和数据访问代码(以及数据库连接机制,而在它)之间的分离.
编写函数的正确方法是函数(或方法)应该做一件事,而不是一次做三件事.
由于这一大块功能,在测试时,您必须同时测试所有这三个,这很困难(请参阅在这些段落中使用"测试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);
Run Code Online (Sandbox Code Playgroud)
这样,为了测试calculate_results中的逻辑(例如,对数据求和),你只需要模拟传递给它的DATA,这很容易(在很多情况下,你只需要在一些测试配置中存储几组测试数据); 而不是模拟用于检索数据的复杂DBI对象的行为.
单独使用DBI没有任何问题.
线索在于这是测试章节.我假设指出的问题是该函数打开并关闭数据库连接本身.它应该期望数据库句柄作为参数,并且只对其运行查询,而不关心打开和关闭与其调用者的数据库连接.这将使功能的工作更加狭窄,因此它使功能更加灵活.
这反过来也使函数更容易测试:只需将模拟对象作为数据库句柄传递给它.正如目前所写,你至少需要重新定义DBI::connect来测试它,这并不难,但肯定是凌乱的.
调用的方法sum_values_per_key应该感兴趣的是对某些键的值求和,而不是获取要求和的数据.
它不符合SOLID编程的S(单一责任原则).http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29
这意味着它是: