论坛首页 综合技术论坛

TDD很痛苦

浏览 24988 次
锁定老帖子 主题:TDD很痛苦
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-02  
iamlotus 写道
xyz20003 写道
DBUnit给我们带来的问题是,它不能自动处理外键关系,表间关系复杂时,每次insert都会报错失败。请问有何解决方案?

DBUnit还有个问题,你在自己测试中插入的数据(而非配置在文件中的)它是不会帮你删的。
正是应该以上原因,我们的数据框架是自己写的。
setup用SQL,自己保证顺序
tearDown后台跑一个存储过程,清空所有表


你没有领会DbUnit的妙处,或者说最佳实践。我记得DbUnit网站上有如下几条最佳实践:

1、每人一个数据库实例。同事之间互不干扰。

2、在运行测试用例前、后,数据库中表都是空的。

3、一个测试方法的运行成败,不能影响到其他测试方法。

回到你的问题,对程序插入的新数据,在测试方法的最后,要使用DbUnit提供的类,对这些数据进行删除。否则的话,在运行下一个测试方法时就可能出问题。
0 请登录后投票
   发表时间:2010-09-02  
先谢谢jinjiankang同志了,之前也试过dbunit提供的对表自动排序,但是速度巨慢,到达无法忍受的地步了。后来遇到的问题更可悲,那就是自表关联的数据,死也倒不进去。

因为我们的测试数据是用dbunit从数据库里export出来的,所以自表关联的数据不一定是外键在前面。后来我们想了个办法,dbunit insert之前,用脚本清除所有外键,insert之后再全部加上。但是表结构一变就要死人啊。

最后就放弃了,用dbunit本来是为了可以自动导入导出,如果这么麻烦,还不如维护一套sql方便。
0 请登录后投票
   发表时间:2010-09-02  
我们现在是这么做

使用spring-test+junit,可以自动回滚数据
单元测试用例基本都是用groovy写,效率提高不少
0 请登录后投票
   发表时间:2010-09-02  
我们的系统要做单元测试也要准备一大堆的数据,其实这个准备过程倒也是可以接受的,但是最恐怖的是如果数据库的结构发生了调整,这些数据的初始化都要跟着改,这个工作量很大。
所以TDD一直是一个让我又爱又恨的东西。
0 请登录后投票
   发表时间:2010-09-03   最后修改:2010-09-03
dcross 写道
我们的系统要做单元测试也要准备一大堆的数据,其实这个准备过程倒也是可以接受的,但是最恐怖的是如果数据库的结构发生了调整,这些数据的初始化都要跟着改,这个工作量很大。
所以TDD一直是一个让我又爱又恨的东西。


可以试试dbdeploy这样的数据库版本控制工具,适合敏捷开发.但是前提是你们以程序为中心而不是数据库为中心。

另外觉得spring 对于测试的支持还不错。我们使用spring同样让数据回滚的方式。
0 请登录后投票
   发表时间:2010-09-03  
dcross 写道
我们的系统要做单元测试也要准备一大堆的数据,其实这个准备过程倒也是可以接受的,但是最恐怖的是如果数据库的结构发生了调整,这些数据的初始化都要跟着改,这个工作量很大。
所以TDD一直是一个让我又爱又恨的东西。


看你怎么改变了,如果是整张表被drop,自然没办法。如果只改动某个字段你却要修改所有牵涉到这张表的数据,那说明你准备数据时插入了太多和测试无关的数据。

比如某张表 T (oid, c1,c2,...,c10)
如果你所有准备数据的方法都是这样 insert T (oid, c1,c2,...,c10) values (1,v1,v2,...v10)。那么一旦c1变成d1,确实所有地方都要修改。 可你真的在每个testcase中都会测到c1吗?不一定吧。
如果在测到c2,c3的地方你这样写insert T (oid, c2,c3) values (1,v2,v3)。那修改c就只会影响到确实测到c1的testcase,也只需修改这部分的准备数据,而不需要修改以上那条数据。
通常我反而会用这个特性,在改了某个字段后跑一遍testcase,看看究竟有多少地方需要修改。

当然,一般schema修改总归要谨慎。如果经常修改的话,可能要重新估量设计了。
0 请登录后投票
   发表时间:2010-09-03  
jinjiankang 写道
iamlotus 写道
xyz20003 写道
DBUnit给我们带来的问题是,它不能自动处理外键关系,表间关系复杂时,每次insert都会报错失败。请问有何解决方案?

DBUnit还有个问题,你在自己测试中插入的数据(而非配置在文件中的)它是不会帮你删的。
正是应该以上原因,我们的数据框架是自己写的。
setup用SQL,自己保证顺序
tearDown后台跑一个存储过程,清空所有表


你没有领会DbUnit的妙处,或者说最佳实践。我记得DbUnit网站上有如下几条最佳实践:

1、每人一个数据库实例。同事之间互不干扰。

2、在运行测试用例前、后,数据库中表都是空的。

3、一个测试方法的运行成败,不能影响到其他测试方法。

回到你的问题,对程序插入的新数据,在测试方法的最后,要使用DbUnit提供的类,对这些数据进行删除。否则的话,在运行下一个测试方法时就可能出问题。

也就是说,把删除(程序)插入数据的责任交给编写testcase的人?我一个写testcase的人为什么要管这些东西?测试框架只要能保证运行每个testcase前数据库是空的,运行完数据库也空的。那么测试之间自然就不会相互影响了,干嘛还要我来保证把屁股擦干净?
我们以前也是由testcase自己擦屁股的,这样的好处是速度快。但实践中发现很不爽,因为我的testcase fail可能是由于你的屁股没擦干净,而你的testcase却没有问题。这样查起问题也很不方便。所以才改成由框架统一擦屁股,这样慢虽然慢一点,但能保证擦干净
0 请登录后投票
   发表时间:2010-09-03   最后修改:2010-09-03
aws 写道
我们现在是这么做

使用spring-test+junit,可以自动回滚数据
单元测试用例基本都是用groovy写,效率提高不少


自动回滚大多情况下没问题,不过对以下问题好像没什么好的解决方法
1)如果testcase fail,你没法在testcase上设个断点,然后用sqlplus去看看数据究竟是什么样的。
2)自动回滚只能回滚当前transaction,如果有个自治事务(取OID什么的)就回不去了。
3)如果你要测个存储过程该怎么解决呢?
0 请登录后投票
   发表时间:2010-09-03  
需要看数据可以在控制台打印出来,groovy做这种事情很方便
实在不行那就手动控制回滚
存储过程本身的逻辑测试用pl/sql developer调试基本就ok了
0 请登录后投票
   发表时间:2010-09-03  
命令式风格(伪代码):
def add_record():
    # 准备数据
    my_obj=MyObj(foo=1,boo=2)
        
    # 存数据库
    conn=get_db_conn()
    return conn.save(my_obj)


函数风格:
def prepare_func():
     return MyObj(foo=1,boo=2)

def db_access_func(obj):
    conn=get_db_conn()
    conn.save(my_obj)

def add_record(prepare_func,db_access_func):
    obj=prepare_func()
    return db_access_func(obj)


上述两种风格,显然函数风格的代码,对add_record函数更容易进行单元测试,原因是更容易建立mock:
def test_add_record():
    mock_prepare_func=lambda:1
    mock_db_func=lambda x: 1
    assert(1,add_record(mock_prepare_func,mock_db_func))


是不是函数式风格的TDD要好一些... 有点爱上函数式风格了 ...

0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics