单元测试“按ID排序”可能吗?

时间:2017-02-07 20:05:29

标签: sql postgresql unit-testing

想象一下,您拥有以下架构和数据:

create table test (
  id serial primary key,
  x integer not null,
  y integer not null
);

insert into test (x, y) values (1, 2), (3, 4), (5, 6), (7, 8);

如果我想以插入的顺序获取test的值,我可以使用:

select * from test order by id;

如果order by id不存在(例如,red/green/refactor之后),我想写一个失败的单元测试。

我的失败的测试会:

  1. 将一些数据插入test
  2. 运行调用select * from test的方法并返回数据(将此更改为select * from test order by id应该使测试通过。)
  3. 验证方法的返回数据与#1中发生的迭代的顺序相同(也就是说id的顺序)。
  4. 但是,此测试有可能返回误报(这通常是表格中没有任何索引的情况)。 order of a query without order by is unspecified

    我想到的一个解决方案是使用id的序列进行黑客攻击,以便id以奇数顺序生成...但这感觉就像是一个黑客只是为了使这个测试可能。

    编辑:好吧,似乎有些混乱。我知道如果没有order by(我甚至引用了文档),订单就无法保证。我无法更改查询的表/视图。我正在修复今天不使用order by id的方法中的错误。但是,我们知道此方法应该使用order by id。因此,我需要更改代码以使用order by id。在我这样做之前,我想编写一个单元测试来重现这个bug(方法的结果不按id排序)。事实上,Postgres正在返回按id排序的数据,因为我猜测数据是按插入顺序从磁盘上传出的。这在测试中创建了一个误报:当它应该失败时,传递。我想消除误报:在我修复bug之前它应该可靠地失败。

2 个答案:

答案 0 :(得分:2)

  

但是,此测试有可能返回误报(这通常是表格中没有任何索引的情况)。

然后在桌面上添加一些索引!据推测,您正在使用自己为这个单元测试实例创建的小数据库,这个数据库将被吹走,随心所欲地做任何事情。索引不会更改查询的保证结果,并且不保证order by订单无法保证其性能。所以这是对环境的安全改变。

  

我想到的一个解决方案是使用id的序列进行黑客攻击,以便以奇怪的顺序生成id ...但这只是为了进行此测试可能的。

单元测试通常会使正常的最佳实践规则陷入困境。在这种情况下,它正在设置您的测试数据,以避免误报。这与创建测试文件没有什么不同,只是为了测试方法中的罕见错误。虽然这使得测试glassbox因为它在存储机制的引擎盖下窥视,但这是一个有效的单元测试技术,用于详细说明。

从序列所在的位置开始。

select last_value from test_id_seq;

然后从那里开始(让我们说出来的话)。

insert into test (id, x, y) values (10, 1, 2), (12, 3, 4), (11, 5, 6), (9, 7, 8);

这会使序列混淆,接下来的几个插入将失败,因此您必须确保通过重新启动序列将其重新置于正轨。

alter sequence test_id_seq restart with 12;

如果其他进程同时与同一个数据库通信,我非常确定这不安全,但这在单元测试中并不重要。在测试结束时把桌子吹走。

同样,尝试删除一些行,然后插入更多行。

因为没有order by 没有保证结果顺序,所以即使以这种方式扰乱数据也不能保证正常工作。相反,迭代各种技术,直到您的数据不按顺序返回。像这样:

def test_ordering
    while `select * from test` is ordered by id
        if we've tried too may times
            warn and go ahead anyway
        end

        perturb the data in test
    end

    assert obj.method is in id order
end

这是你能做的最好的事情。在某一点上,你必须减少损失。

答案 1 :(得分:0)

您无法从查询结果中判断该查询是否包含ORDER BY

只有在ORDER BY中指定此顺序时,才能保证查询以特定顺序返回行。因此,您的初始假设是错误的,并且无法查看查询的结果并从中确定是否应用了ORDER BYORDER BY可能会丢失,正确的行顺序只是巧合。

即使你想欺骗它并从排序视图中选择,这也无济于事。如果您在select * from test order by id desc上创建了一个视图,然后检查此视图上的查询是否返回 asccending 行,那么这将没有任何意义,因为

select * from test_view;

基本上是

select * from (select * from test order by id desc);

因此您可以从派生表中进行选择,根据定义,派生表本身也是一组无序数据(即内部的ORDER BY子句对结果没有保证效果。)