1.背景在某次系统测试中,当部分保单被选中并更新为紧急状态时,未选中的保单也同步更新为紧急状态。经过对比分析,发现SQL查询被设计为数据库中的字符型字段。SQL语句使用数值类型查询时,查询结果最后会出现比较不一致的值。价值观:凡事必有果。通过度娘,我首先了解到,在MySQL中,当运算符与不同类型的操作数一起使用时,会发生类型转换,使操作数兼容,即字符字段和数值类型比较值时,会进行隐式类型转换:全部转换为数值类型。即当数据库字段值为字符类型1234,查询条件为数值1234时,将数据库字符类型1234转换为数值类型1234,即可查询对应的值。但是为什么数值类型100320201000195806除了100320201000195806之外还有100320201000195812这样的字符结果呢?2.考虑到值可能溢出,将问题根因子的值隐式转换为double类型。按照DOUBLE类型,占用8个字节(64位),需要1位表示符号,11位表示指数,2的52次方-1:2^53=9007199254740992。当输入值大于9007199254740992,存在值溢出。也就是说,当数字超过16位时,数据库不会报错,而是溢出后转换为最接近的值存入数据库,如下图:设置字段类型为double,而9007199254740993存储后会显示为9007199254740992。而下图的测试可以看出,溢出入库的数据转换后有一些没有明显规律的规则:同理,在取数据时,大于9007199254740992的值也会被转换,如图下面,9007199254740993可以取到9007199254740992的值:所以bug生成过程如下:18位值类型为double类型(1.003202010001958E+18),查询数据库时将business_no改为(1.003202010001958E+18),数据库存的business_no值也按照隐式类型转换的原则转换为double类型。最终,过滤器的最后两位数的值是相等的。至此,对不同字符产生的隐式类型转换,过程中转换为double类型,数值溢出问题有了更新的认识。三、从bug的出现来思考测试点1、逆向思维上面是一个从字符型字段导入数值的案例,因为字符型字段也是存储值的,所以查询不会出现其他异常不超过16位数字。但是,如果数字字段导入字符字段怎么办?下图测试:插入id=0的数据后,queryid=a可以查询到id=0的数据。原来mysql的隐式转换,在int类型字段中传入字符串时,会截取从第一个int类型到第一个非int类型值的值作为条件。因为a没有int值,等于0,最终查询id=a可以查询到id=0的数据。特别注意:如果输入字符串为01e2,则可以查询100。2、对于隐式转换的bug,如何优化用例测试点,积极防范,提高测试覆盖率?(1)功能上的考虑:表单验证:使用与数据库设计类型不同的数据进行测试,如数据库数值类型,使用字符数据进行测试,字符数据使用0或&等数值运算符进行测试,从源头尝试过滤不一致。对于非web入口的来源或者后台程序代码看不到的类型转换问题,可以使用多个相邻的大于16位的数字和按照隐式转换规则转换后值一致的字符串来进行数据铺设。测试点包括涉及后台查询的所有场景。在我们的保险软件测试中,由于单据号是非常重要的基础数据,所以对于保单号、保险申请书等16位以上的单据的准备要特别注意。另外要注意数值统计和时间对比的业务场景,因为涉及到时间和数值统计时往往需要函数进行转换。这时候如果格式不一致,也会出现隐式转换问题:比如date(a.publish_time)>=date_sub(curdate(),INTERVAL1DAY)日期显示格式:YYYY-MM-DD;日期时间显示格式:YYYY-MM-DDHH:mm:ss。所以2008-12-2716:25:46.635被dat(e)函数处理为:2008-12-27,date_sub时间值精确到秒,所以发生隐式转换后,date()会被转换到2008-12-270:0:0,时间范围缩小。数据库中存在NULL值时要特别注意。因为当两个参数至少有一个为NULL时,比较的结果也为NULL。
