Как протестировать ПРОЦЕДУРУ в PostgreSQL с помощью pgTAP? - PullRequest
0 голосов
/ 13 декабря 2018

Есть ли лучшая практика для модульного тестирования PostgreSQL 11+ PROCEDURE (НЕ FUNCTION ) с использованием 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.

1 Ответ

0 голосов
/ 15 декабря 2018

Вот подход, который я придумала, вдохновленный использованием интерфейсов вместе с мокинг-фреймворками на других языках.

Сначала мы переместим операцию 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;

Идея состоит в том, чтобы временно смоделировать фактические вызовы функций для тестирования.
Таким образом, можно выполнить модульное тестирование хранимой процедуры без совершения реальных транзакций.
Когда тест заканчивается, он откатывает транзакцию и все изменения отменяются.

...