模拟Postgresql now()函数进行测试

时间:2018-01-13 20:18:51

标签: node.js postgresql mocha sinon

我有以下堆栈

  • 节点/快速后端
  • Postgresql 10数据库
  • 摩卡测试
  • Sinon for mocking

我写了一堆端到端的测试来测试我的所有web服务。问题是它们中的一些是时间依赖的(如在"给我修改最后X秒的记录")。

sinon非常善于在Node中模拟所有时间/日期相关的东西,但是我的Postgresql表中有一个modified字段,其中填充了一个触发器:

CREATE FUNCTION update_modified_column()
  RETURNS TRIGGER AS $$
BEGIN
  NEW.modified = now();
  RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';

问题当然是sinon无法覆盖now()函数。

关于如何解决这个问题的任何想法?问题不是在测试开始时设置一个特定的日期,而是比实时更快地推进时间(在我的一个测试中,我想改变数据库中的一些东西,推进当前时间'有一天,在数据库中更改一些东西并进行webservice调用以查看结果。

我自己可以找出一些解决方案,但它们都涉及更改应用程序代码并使其不那么优雅。我不认为您的应用程序代码会受到您要测试它的影响。

3 个答案:

答案 0 :(得分:1)

老实说,DB内部的东西总是很难从应用程序代码中测试。根据我的经验,最好的办法就是验证记录的状态。

因此,不是专门测试内部调用now函数,而是编写一个创建新记录的测试,然后验证该记录是否已创建和修改了字段集。在那一点上,它们应该相互平等,并且可能在最后一两秒内,所以你可以写出断言的所有东西。

然后你编写另一个测试来改变记录中的某些值,并写出修改后的图章与创建的图章不同的断言,更新,并且可能在最后一秒或两秒内。

答案 1 :(得分:0)

如果稍微更改触发器功能以使用条件语句,您可以解决您的问题吗?

这意味着如果您提供特定的修改时间戳,该表将使用该值。

例如:

CREATE TABLE tbl (
id SERIAL PRIMARY KEY,
some_col TEXT NOT NULL,
modified TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
);


CREATE FUNCTION update_modified_column()
  RETURNS TRIGGER AS $$
BEGIN
    IF NEW.modified IS NULL THEN
        NEW.modified := now();
    END IF;
  RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER modified_trg 
    BEFORE UPDATE ON tbl 
    FOR EACH ROW 
EXECUTE PROCEDURE update_modified_column();


INSERT INTO tbl (some_col)
VALUES ('test 1'), ('test 2'), ('test 3');

SELECT * FROM tbl;

 id | some_col |           modified
----+----------+-------------------------------
  1 | test 1   | 2018-01-13 21:49:25.411572+00
  2 | test 2   | 2018-01-13 21:49:25.411681+00
  3 | test 3   | 2018-01-13 21:49:25.411686+00
(3 rows)

UPDATE tbl
  SET 
    some_col='UPDATED', 
    modified = '2018-02-01 00:00:00+00' 
  WHERE id = 2;

SELECT * FROM tbl;
 id | some_col |           modified
----+----------+-------------------------------
  1 | test 1   | 2018-01-13 21:49:25.411572+00
  3 | test 3   | 2018-01-13 21:49:25.411686+00
  2 | UPDATED  | 2018-02-01 00:00:00+00
(3 rows)

答案 2 :(得分:0)

这里有一个想法:使用 mock_now() 表创建您自己的 mock_dates

create table mock_dates (
    id serial PRIMARY KEY,
    mock_date timestamptz not null
);

create or replace function mock_now()
    returns timestamptz
    as $$
    declare
        RET timestamptz;
    begin
        -- Delete first added date and assign it to RET
        delete from mock_dates where id in (
            select id from mock_dates order by id asc limit 1
        )
        returning mock_dates.mock_date into RET;

        -- If no deletion happened just return the current timestamp
        if RET is null then
            return now();
        end if;

        -- Otherwise return the mocked date
        return RET;
    end;
$$
language plpgsql;

并插入一些模拟日期

insert into mock_dates (mock_date) values ('2001-03-11 02:34:00'::timestamptz);
insert into mock_dates (mock_date) values ('2002-05-22 01:49:00'::timestamptz);

并使用 mock_now() 而不是 now()。它将返回插入到 mock_dates 表中的时间戳一次(先进先出)。

当表为空时,它将像默认的 now() 一样工作。

只需确保 mock_dates 表在生产环境中是空的 ?

或者您甚至可以为生产定义一个不同的函数,它甚至不尝试读取 mock_dates 表。

相关问题