Team1工作记录

<pre><...
<pre><code>标签似乎有问题,我在最后一段代码上加标签的时候有显示错误,会重复显示一些东西,很乱

下面是翻译的文章:

翻译:SaladJonk
出处:中国 Perl 协会 FPC - PerlChina.org
原 名:Perl Code Kata: Testing Databases
中 文:Perl编程技巧-与数据库相关的代码测试
作 者:Stevan Little
原 文:http://www.perl.com/lpt/a/2005/02/10/database_kata.html
发 表:2005年2月10日

Perl Code Kata: Testing Databases
By Stevan Little
February 10, 2005

Perl编程技巧-与数据库相关的代码测试
作者:Stevan Little
2005年二月10日

Testing code that uses a database can be tricky. The most common solution is to set up a test database with test data and run your tests against this. This, of course, requires bookkeeping code to keep your test database in the proper state for all your tests to run without adversely affecting one another. This can range from dropping and recreating the test database for each test, to a more granular adding and deleting at the row level. Either way, you are introducing non-test code into your tests that open up possibilities for contamination. Ultimately, because you have control over the environment in which your tests run, you can manage this despite the occasional headache.

The real fun only starts when you decide that you should release your masterpiece unto the world at large. As any CPAN author will tell you, it is absolutely impossible to control the environment other people will run your code in once you release it. Testing database code in such a hostile environment can be frustrating for both the module developer and the module installer. A common approach is to allow the user to specify the specific database connection information as either environment variables or command-line arguments, skipping the tests unless those variables are present. Another approach is to use the lightweight and very portable SQLite as your test database (of course, testing first that the user has installed SQLite). While these solutions do work, they can often be precarious, and in the end will increase the number of possible installation problems you, as module author, could face.

What is a module author to do?

测试与数据库相关的代码是很需要技巧的。最常见的方法是为你的程序建立一个由测试数据组成的测试数据库,而且建立起来的这个测试数据库必须能保证使你的测试工作,从删除重建一个数据库到更小的添加删除一个行,互不干扰。但不管怎样,你都会引入一些与测试无关的代码,而它们可能导致变量污染。但是由于你能完全控制待测代码的运行环境,偶尔出现的这些问题不会引起太大麻烦。

在你把你的杰作向全世界发布的时候,真正的问题才会到来。就像很多 CPAN 上模块的作者将会告诉你的一样,一旦你发布了你的模块,你不可能控制用户使用这些模块的环境。在一个如此不可知的环境中运行你的那些与数据库相关的代码,不论对用户还是作者本人都是一种挑战。一个通常的方法是,用户在使用你的代码的时候,可以通过环境变量或命令行输入特定的数据库连接信息来指明运行环境,当然如果环境变量已经被指明,就可以跳过这一步。另一个方法是使用一个轻量级,有很好可移植性的数据库 SQLite 来作为你的测试数据库(当然,也要求用户安装 SQLite )。当你使用以上这些方案的时候,仍然很危险,最后,作为模块的作者,你可能会面临很多与安装相关的问题。

那么模块的作者应该怎么办呢?

DBD::Mock Testing Kata

This code kata introduces an alternate approach to testing database code, that of using mock-objects, and specifically of using the DBD::Mock mock DBI driver. Before showing off any code, I want to explain the basic philosophy of Mock Objects as well as where DBD::Mock fits in.

Related Reading

Computer Science & Perl Programming
Best of The Perl Journal
Table of Contents
Sample Chapter
What are Mock Objects?

When writing unit tests, it is best to try to isolate what you are testing as much as possible. You want to be sure that not only are you only testing the code in question, but that a bug or issue in code outside what you are testing will not introduce false negatives in your tests. Unfortunately, this ideal of a completely decoupled design is just an ideal. In real-world practice, code has dependencies that you cannot remove for testing. This is where Mock Objects come in.

Mock Objects are exactly what they sound like; they are "mocked" or "fake" objects. Good polymorphic thought says that you should be able to swap out one object for another object implementing the same interface. Mock Objects take advantage of this by allowing you to substitute the most minimally mocked implementation of an object possible for the real one during testing. This allows you to concentrate on the code being tested without worrying about silly things, such as whether your database is still running or if there is a database available to test against.
Where Does DBD::Mock Fit In?

DBD::Mock is a mock DBI Driver that allows you to test code which uses DBI without needing to worry about the who, what, when, and where of a database. DBD::Mock also helps to reduce the amount of database bookkeeping code by doing away with the database entirely, instead keeping a detailed record of all the actions performed by your code through DBI. Of course, database interaction/communication is not only one way, so DBD::Mock also allows you to seed the driver with mock record sets. DBD::Mock makes it possible to fake most (non-vendor specific) database interaction for the purpose of writing tests. For more detailed documentation I suggest reading the DBD::Mock POD documentation itself.
Sample DBI Code

In the tradition of past Perl Code katas here is some simplified code to write your tests against. This code should be simple enough to understand, but also complex enough to show the real usefulness of DBD::Mock.

package MyApp::Login;

use DBI;

my $MAX_LOGIN_FAILURES = 3;

sub login {
my ($dbh, $u, $p) = @_;
# look for the right username and password
my ($user_id) = $dbh->selectrow_array(
"SELECT user_id FROM users WHERE username = '$u' AND password = '$p'"
);
# if we find one, then ...
if ($user_id) {
# log the event and return success
$dbh->do(
"INSERT INTO event_log (event) VALUES('User $user_id logged in')"
);
return 'LOGIN SUCCESSFUL';
}
# if we don't find one then ...
else {
# see if the username exists ...
my ($user_id, $login_failures) = $dbh->selectrow_array(
"SELECT user_id, login_failures FROM users WHERE username = '$u'"
);
# if we do have a username, and the password doesnt match then
if ($user_id) {
# if we have not reached the max allowable login failures then
if ($login_failures < $MAX_LOGIN_FAILURES) {
# update the login failures
$dbh->do(qq{
UPDATE users
SET login_failures = (login_failures + 1)
WHERE user_id = $user_id
});
return 'BAD PASSWORD';
}
# otherwise ...
else {
# we must update the login failures, and lock the account
$dbh->do(
"UPDATE users SET login_failures = (login_failures + 1), " .
"locked = 1 WHERE user_id = $user_id"
);
return 'USER ACCOUNT LOCKED';
}
}
else {
return 'USERNAME NOT FOUND';
}
}
}

There are four distinct paths through this code, each one resulting in one of the four return messages; LOGIN SUCCESSFUL, BAD PASSWORD, USER ACCOUNT LOCKED, and USERNAME NOT FOUND. See if you can write tests enough to cover all four paths. Feel free to use Devel::Cover to verify this.

Armed with your knowledge of DBD::Mock, go forth and write tests! The next page describes DBD::Mock in more detail and gives some strategies for writing the appropriate tests. You should spend between 30 and 45 minutes writing the tests before continuing.

by Stevan Little

DBD::Mock 一个虚拟数据库驱动模块

这个模块引入了另一种测试与数据库相关的代码的方法:使用虚拟对象,准确的说是使用 DBD::Mock 提供的虚拟 DBI 驱动程序。在列出代码之前,我想先解释一些虚拟对象的基本知识然后介绍一下 DBD::Mock 的实用范围。

虚拟对象是什么?

当你编写单元测试的时候,你总是尽可能的把要测试的单元与其它单元隔离开,防止其它非测试的单元引入一些虚假的信息。但是很不幸,做到程序模块间互不影响只是一个美好的梦。在真实的实践中,代码都是相互间存在联系的,而在测试中你不可能隔断这种联系。所以虚拟对象的方法就应运而生了。

虚拟对象就像它的字面意思一样,它们是虚拟的,假的对象。优秀的多形思想认为,你的程序应该作到用一个对象代替另一个对象后,程序提供的接口仍然不变。虚拟对象使用一个对象的最小虚拟实现来代替它本身做了这一点,而且做的很好。这就使得你可以集中精力在要被测试的代码上,而不用操心其它一些琐事,比如数据库是否在运行啊,是否有可用数据库啊等。

DBD::Mock实用在哪些地方?

DBD::Mock 是一个虚拟的 DBI 驱动程序,任何在代码中使用了 DBI 模块的程序都可以使用它,它使得你不用担心 DBI 所连接的后台数据库的一切详细信息。由于完全不与后台数据库打交道,DBD::Mock 可以使你的程序大大简化,取而代之,程序只记录你通过 DBI 与数据库的交互记录。当然,数据库的交互是双向的,所以 DBD::Mock 允许你向这个虚拟的驱动程序提供虚拟的记录集。DBD::Mock 能够虚拟出与绝大多数数据库的交互。如果你想要阅读更仔细的文档,我建议你自己阅读 DBD::Mock 的 POD 文档

一段使用了 DBI 的样板程序
按照习惯,这里首先写出一段需要测试的与数据库相关的代码。这段代码简单容易理解,但是不会太简单,那样就体现不了 DBD::Mock 的用处了。

package MyApp::Login;

use DBI;

my $MAX_LOGIN_FAILURES = 3;

sub login {
my ($dbh, $u, $p) = @_;
# look for the right username and password
my ($user_id) = $dbh->selectrow_array(
"SELECT user_id FROM users WHERE username = '$u' AND password = '$p'"
);
# if we find one, then ...
if ($user_id) {
# log the event and return success
$dbh->do(
"INSERT INTO event_log (event) VALUES('User $user_id logged in')"
);
return 'LOGIN SUCCESSFUL';
}
# if we don't find one then ...
else {
# see if the username exists ...
my ($user_id, $login_failures) = $dbh->selectrow_array(
"SELECT user_id, login_failures FROM users WHERE username = '$u'"
);
# if we do have a username, and the password doesnt match then
if ($user_id) {
# if we have not reached the max allowable login failures then
if ($login_failures < $MAX_LOGIN_FAILURES) {
# update the login failures
$dbh->do(qq{
UPDATE users
SET login_failures = (login_failures + 1)
WHERE user_id = $user_id
});
return 'BAD PASSWORD';
}
# otherwise ...
else {
# we must update the login failures, and lock the account
$dbh->do(
"UPDATE users SET login_failures = (login_failures + 1), " .
"locked = 1 WHERE user_id = $user_id"
);
return 'USER ACCOUNT LOCKED';
}
}
else {
return 'USERNAME NOT FOUND';
}
}
}

这段代码最后可能产生四种不同的结果,它们是:登陆成功,密码错误,琐定用户和用户不存在。自己试着对这段程序写一个测试,看是否能涵盖这四种情况,你可以用 Devel::Cover 模块来检查你的结果。

利用你在DBD::Mock的知识,再写一个测试!后面的部分将详细介绍 DBD::Mock 模块,并且会给你一些写测试的策略。在这之前,你应该先花 30 到 45 分钟写测试。

代码作者:Stevan Little

Tips, Tricks, and Suggestions

Because DBD::Mock is an implementation of a DBD driver, its usage is familiar to that of DBI. DBD::Mock is unique in its ability to mock the database interaction. The following is a short introduction to these features of DBD::Mock.

Fortunately, connecting to the database is the only part of your regular DBI code which needs to be DBD::Mock specific, because DBI chooses the driver based upon the dsn string given it. To do this with DBD::Mock:

my $dbh = DBI->connect('dbi:Mock:', '', '');

Because DBI will not actually connecting to a real database here, you need no database name, username, or password. The next thing to do is to seed the database driver with a result set. Do this through the mock_add_resultset attribute of the $dbh handle.

$dbh->{mock_add_resultset} = [
Synopsis 3: Pe...
Synopsis 3: Perl 6 中的操作符
我翻译这一篇,好象比较短:)
翻译完成了...
领一个任务
perl.com: Photo Galleries with Mason and Imager
http://www.perl.com/pub/a/2004/04/01/masongal.html

共四篇预计两周
人呢?翻到什么程度了?没.
人呢?翻到什么程度了?没看到结果哦[CCB]10[/CCB]
目前正在移植 wiki ,以完.
目前正在移植 wiki ,以完成大半。中文化已经可以继续进行了。请访问 wiki.perlchina.org