如何使用 pgTAP 在 PostgreSQL 中测试 PROCEDURE?
How to test PROCEDURE in PostgreSQL with pgTAP?
是否有对 PostgreSQL 11+ 进行单元测试的最佳实践PROCEDURE (NOT a FUNCTION) using pgTap。
例如,如何推荐对这样的存储过程进行单元测试:
CREATE OR REPLACE PROCEDURE foo.do_something(IN i_value INT)
AS
$$
BEGIN
PERFORM foo.call_function_1(i_value);
COMMIT;
PERFORM foo.call_function_2(i_value);
COMMIT;
CALL foo.another_procedure(i_value);
END;
$$
LANGUAGE plpgsql;
这变得很困难,因为 pgTap 单元测试 运行 通过这样的存储函数:
SELECT * FROM runtests('foo'::NAME);
这在事务中执行,使得无法执行通过调用COMMIT
或ROLLBACK
修改事务状态的存储过程。
这是我想出的一种方法,灵感来自于使用接口以及其他语言的模拟框架。
首先我们将 COMMIT
操作移动到这样的存储过程中:
CREATE PROCEDURE foo.do_commit()
AS
$$
BEGIN
COMMIT;
END;
$$
LANGUAGE plpgsql;
然后我们改变实际的存储过程来调用do_commit
而不是直接使用COMMIT
命令。例如:
CREATE OR REPLACE PROCEDURE foo.do_something(IN i_value INT)
AS
$$
BEGIN
PERFORM foo.call_function_1(i_value);
CALL foo.do_commit();
CALL foo.another_procedure(i_value);
END;
$$
LANGUAGE plpgsql;
由于单元测试是在回滚的事务中执行的,我们可以暂时将 do_commit
调用替换为用于测试的模拟对象。测试可能看起来像这样:
CREATE FUNCTION test.test_do_something()
RETURNS SETOF TEXT
AS
$$
BEGIN
CREATE TEMPORARY TABLE commit_calls
(
commit_call BOOLEAN NOT NULL DEFAULT TRUE
)
ON COMMIT DROP;
CREATE TEMPORARY TABLE function_calls
(
the_value INT NOT NULL
)
ON COMMIT DROP;
CREATE OR REPLACE PROCEDURE foo.do_commit()
AS
$mock_do_commit$
BEGIN
INSERT INTO commit_calls (commit_call)
VALUES (DEFAULT);
END;
$mock_do_commit$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION foo.call_function_1(i_value INT)
RETURNS VOID
AS
$mock_call_function_1$
INSERT INTO function_calls (the_value)
VALUES (i_value);
$mock_call_function_1$
LANGUAGE sql;
-- EXECUTE
CALL foo.do_something(9);
CALL foo.do_something(100);
-- VERIFY
RETURN NEXT assert.is((SELECT COUNT(*) FROM commit_calls)::INT, 2, 'verify transaction commits');
RETURN NEXT assert.bag_eq(
'SELECT the_value FROM function_calls',
'VALUES (9), (100)',
'verify function call values');
END;
$$
LANGUAGE plpgsql;
想法是暂时模拟实际的函数调用以进行测试。
这样就可以在不提交实际事务的情况下对存储过程进行单元测试。
当测试结束时,它会回滚事务并丢弃所有更改。
是否有对 PostgreSQL 11+ 进行单元测试的最佳实践PROCEDURE (NOT a FUNCTION) using pgTap。
例如,如何推荐对这样的存储过程进行单元测试:
CREATE OR REPLACE PROCEDURE foo.do_something(IN i_value INT)
AS
$$
BEGIN
PERFORM foo.call_function_1(i_value);
COMMIT;
PERFORM foo.call_function_2(i_value);
COMMIT;
CALL foo.another_procedure(i_value);
END;
$$
LANGUAGE plpgsql;
这变得很困难,因为 pgTap 单元测试 运行 通过这样的存储函数:
SELECT * FROM runtests('foo'::NAME);
这在事务中执行,使得无法执行通过调用COMMIT
或ROLLBACK
修改事务状态的存储过程。
这是我想出的一种方法,灵感来自于使用接口以及其他语言的模拟框架。
首先我们将 COMMIT
操作移动到这样的存储过程中:
CREATE PROCEDURE foo.do_commit()
AS
$$
BEGIN
COMMIT;
END;
$$
LANGUAGE plpgsql;
然后我们改变实际的存储过程来调用do_commit
而不是直接使用COMMIT
命令。例如:
CREATE OR REPLACE PROCEDURE foo.do_something(IN i_value INT)
AS
$$
BEGIN
PERFORM foo.call_function_1(i_value);
CALL foo.do_commit();
CALL foo.another_procedure(i_value);
END;
$$
LANGUAGE plpgsql;
由于单元测试是在回滚的事务中执行的,我们可以暂时将 do_commit
调用替换为用于测试的模拟对象。测试可能看起来像这样:
CREATE FUNCTION test.test_do_something()
RETURNS SETOF TEXT
AS
$$
BEGIN
CREATE TEMPORARY TABLE commit_calls
(
commit_call BOOLEAN NOT NULL DEFAULT TRUE
)
ON COMMIT DROP;
CREATE TEMPORARY TABLE function_calls
(
the_value INT NOT NULL
)
ON COMMIT DROP;
CREATE OR REPLACE PROCEDURE foo.do_commit()
AS
$mock_do_commit$
BEGIN
INSERT INTO commit_calls (commit_call)
VALUES (DEFAULT);
END;
$mock_do_commit$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION foo.call_function_1(i_value INT)
RETURNS VOID
AS
$mock_call_function_1$
INSERT INTO function_calls (the_value)
VALUES (i_value);
$mock_call_function_1$
LANGUAGE sql;
-- EXECUTE
CALL foo.do_something(9);
CALL foo.do_something(100);
-- VERIFY
RETURN NEXT assert.is((SELECT COUNT(*) FROM commit_calls)::INT, 2, 'verify transaction commits');
RETURN NEXT assert.bag_eq(
'SELECT the_value FROM function_calls',
'VALUES (9), (100)',
'verify function call values');
END;
$$
LANGUAGE plpgsql;
想法是暂时模拟实际的函数调用以进行测试。
这样就可以在不提交实际事务的情况下对存储过程进行单元测试。
当测试结束时,它会回滚事务并丢弃所有更改。