我有以下堆栈
我写了一堆端到端的测试来测试我的所有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调用以查看结果。
我自己可以找出一些解决方案,但它们都涉及更改应用程序代码并使其不那么优雅。我不认为您的应用程序代码会受到您要测试它的影响。
答案 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
表。