技术开发 频道

浅析数据库程序的单元测试

    你需要四个数据库

    有些想法认为一个好的测试是足够充分的并能建立测试所需要的全部数据。如果你能在测试进行前就明确知道数据库所处的状态,测试可以进行一些简化。一个简化的方法是建立一个独立的单元测试数据库用于测试程序,测试程序在开始进行前清除测试数据库中的全部数据。

    在代码中,你可以编写一个dbSetUp方法,如下所示:

public void dbSetUp()
{
// Put the database in a known state:
// (stored procedures would probably be better here)
helper.exec("DELETE FROM SomeSideTable");
helper.exec("DELETE FROM User");
// Insert some commonly-used test cases:
...
}

    任何数据库测试程序都将在做任何事前首先调用dbSetUp方法,它将使测试数据库处于一种已知状态(大部分情况下是空数据库状态)。这种做法具有以下的优点:

    所有的测试数据都在代码层和其他编程人员进行交流,因此没有必要进行外部测试数据协调。

    无须测试用的特殊数据的介入。

    简单而容易理解的一种方法。

    在每一次测试前删除和插入数据可能会花较多时间,但是由于测试用的数据量相对较小,我认为这种方法比较快捷,特别是在测试一个本地数据库时。

    这种做法不利的一面是你需要至少两个数据库。但是请记住,他们在必要是都可以在同一个服务器上运行。采用这种方法,我用了四个数据库,另外两个在紧急关头时使用,具体如下:

    1.实际使用数据库,包含实际数据。在这个数据库中不进行测试,确保数据的完整性。

    2.你的本地开发数据库,用来进行大部分的测试。

    3.一个加入一定量数据的本地开发数据库,可能和其他编程人员共享,用来运行应用程序并检测是否能在实际使用的数据库上运行,而不是照搬实际使用数据库中的全部数据。从严格意义上说你可能并不需要这一数据库,但这一数据库能确保应用程序在有大量数据的数据库中顺利运行。

    4.一个发布数据库,或称集成数据库,用来在正式发布前进行一系列测试,从而确保对所有本地数据库的修改都得到确认。如果你一个人开发,你可以省略这个数据库,但你必须确保所有对数据结构和存储过程的修改都在实际使用数据库中得到确认。

    在有多个数据库的情况下,你要确保不同数据库间结构的同步:如果你在测试数据库中改变表的定义或存储过程,你必须记得在实际使用的服务器上进行同样的修改。发布数据库的作用就是提醒你进行这些修改。另外,我发现如果代码控制系统能将提交时的注释用邮件形式自动发给整个开发组,那将给团队开发带来较大帮助。CVS就能做到这一点,我希望你能利用这一功能。

    在合适的数据库中进行测试

    在这种情况下,你必须连接正确的数据库。在实际使用数据库中进行测试有可能删除所有的有用数据,这点令我十分害怕。

    有几种办法能避免此类悲剧的发生。例如,比较普遍的做法是将数据库连接设置记录在初始文件中,从而明确哪一个是测试数据库。你也可以通过初始文件进行本地数据库的测试,而用其他指定方法连接实际使用数据库。

    在java代码中,初始文件可能如下所示;

    myapp.db.url=jdbc:mysql://127.0.0.1/mydatabase

    这一连接字符串用来连接数据库。你可以添加第二个连接字符串来区分测试数据库:

    myapp.db.url=jdbc:mysql://127.0.0.1/mydatabase
    myapp.db.testurl=jdbc:mysql://127.0.0.1/my_test_database

    在测试代码中,你可以检查并确保在连接到测试数据库后应用程序才能继续运行:

public void dbSetUp()
{
String test_db = InitProperties.get("myapp.db.testurl");
String db = InitProperties.get("myapp.db.url");
if (test_db == null)
abort("No test database configured");
if (test_db.equals(db))
{
// All is well: the database we''re connecting to is the
// same as the database identified as "for testing"
}
else
{
abort("Will not run tests against a non-test database");
}

    另一个技巧是,如果你有一个本地测试数据库,测试程序能通过提供IP地址或主机名进行检测。如果不是“localhost/127.0.0.1”,这就有连接在实际使用数据库上进行测试的风险。

    关于测试日期的体会

    如果你想存储日期信息,你大概想确认你存的日期信息是否正确。请注意以下几点。

    首先先问自己,是谁创建该日期。如果是你的应用程序,那验证比较简单,因为你可以通过查看数据库中的具体日期进行比较。如果是数据库本身创建该日期,可能作为一个缺省字段,那你可能就会有些问题。例如,你能确保你代码所代表的时区和数据库的时区一致吗?从没有听说过数据库是以格林尼治标准时间为准显示时间和日期的。你能确保运行应用程序的计算机上的时间和数据库所在计算机上的时间保持一致吗?如果不是,你必须在进行时间的比较时留出一定的误差。

    如果你遇到这些情况,有些事是你可以做的:

    如果你预先知道所用的时区,在测试前将所有日期和时间全部转换成那个时区的日期和时间。

    在比较时间时设立一定的误差,比如说几分钟、几小时或几个月。看上去缺乏说服力,但至少它能捕获诸如日期为空或1970年1月1日等错误。

    总结

    在本文中,我想说:

    单元数据库测试是一件值得做的事;

    如果你能给一系列测试程序一个对应的数据库,测试本身并不非常可怕。

    还有其他方法能解决这一问题。我还不能确信模仿对象(Mock Object)这一方法。就我对这一方法的理解,模仿对象模拟了一个系统中间层(在本文中,是数据库操作系统),使得模仿的数据库总能返回你想要的数据。我比较欣赏这一概念,它鼓励你对测试进行分层,可能划分成SQL方面的测试和Java语言方面的测试,从而对模仿的ResultSet对象进行测试。

    我比较关注那些能导致一次能使两个或两个以上的数据表产生变化的操作。在这种情况下,用模仿对象方法进行数据库的维护和实现比较困难。当然,我还要找到一种好方法进行数据库中SQL方面的测试,从而确认数据被正确地存储到数据库中。

0
相关文章