Я создаю различные модульные тесты для нашей базы данных, используя платформу tSQLt. Многие из наших хранимых процедур вставляют записи в различные таблицы. Эти процедуры напрямую вызывают наши последовательности для создания идентификаторов вставляемых записей:
SET tID = NEXT VALUE dbo.TableName_SEQ
При создании модульного теста для процедур, ссылающихся на эту последовательность, я попытался использовать tSQLt.RemoveObject, но получил ошибку:
Объект «[dbo].[TableName_SEQ]» невозможно переименовать, поскольку объект участвует в принудительных зависимостях.
Поскольку процедура, которую я тестирую, напрямую использует эту последовательность, как мне с этим справиться? Существует ли какой-либо тип объекта tSQLt, который может подделать последовательность, например «FakeFunction», и вводить желаемый идентификатор каждый раз при ее запуске?
На данный момент этого нет. Это та же проблема, что и с индексированными представлениями, которые также используют привязку схемы. Я начал заниматься этим автоматически, но расчетного времени прибытия нет.
На данный момент у вас есть два варианта. Один из них — вручную разорвать привязку схемы. Вы можете сделать это с помощью этого оператора:
SELECT
'ALTER TABLE dbo.mytable DROP CONSTRAINT '+QUOTENAME(DC.name)+';' [drop],
'ALTER TABLE dbo.mytable '+
'ADD '+
CASE WHEN DC.is_system_named = 1 THEN '' ELSE 'CONSTRAINT '+QUOTENAME(DC.name)+' ' END+
'DEFAULT '+DC.definition+' FOR '+QUOTENAME(C.name)+';' [recreate]
FROM sys.default_constraints DC
JOIN sys.columns C
ON DC.parent_object_id = C.object_id
AND DC.parent_column_id = C.column_id
WHERE parent_object_id = OBJECT_ID('dbo.mytable')
AND definition LIKE '%[[]dbo].[[]mytable_seq]%';
Это даст вам два утверждения, которые выглядят следующим образом:
ALTER TABLE dbo.mytable DROP CONSTRAINT [DF__mytable__id__42ACE4D4];
и
ALTER TABLE dbo.mytable ADD DEFAULT (NEXT VALUE FOR [dbo].[mytable_seq]) FOR [id];
или
ALTER TABLE dbo.mytable ADD CONSTRAINT [DF__mytable__id__42ACE4D4] DEFAULT (NEXT VALUE FOR [dbo].[mytable_seq]) FOR [id];
(Получите ли вы первую или вторую команду повторного создания, зависит от того, было ли ограничение изначально именованным ограничением.)
Теперь в вашем тесте вы можете выполнить оператор drop. Затем подделайте стол. Затем используйте tSQLt.RemoveObject
, чтобы удалить последовательность. Теперь вы можете создать макет последовательности (с тем же именем, что и оригинал). Наконец, выполните оператор re-create.
Теперь вы готовы выполнить тестируемый код.
Предупреждение: хотя tSQLt выполняет все тесты в транзакции и при этом автоматически возвращает все на место в конце теста, это не является надежным. Предлагаю вставить дроп и создать операторы в какую-нибудь таблицу. Таким образом, вы сможете добраться до них, если автоматика ROLLBACK
выйдет из строя.
Это долгий путь. Есть более простой способ: вариант 2. Однако этот способ потенциально не лишен побочных эффектов. Это утверждение ALTER SEQUENCE
:
DROP TABLE IF EXISTS dbo.mytable;
IF(OBJECT_ID('dbo.mytable_seq')IS NOT NULL)DROP SEQUENCE dbo.mytable_seq;
GO
CREATE SEQUENCE dbo.mytable_seq AS INT START WITH 100 CACHE 10;
CREATE TABLE dbo.mytable(id INT DEFAULT NEXT VALUE FOR dbo.mytable_seq,col2 INT);
GO
INSERT INTO dbo.mytable DEFAULT VALUES;
INSERT INTO dbo.mytable DEFAULT VALUES;
GO
BEGIN TRAN;
GO
ALTER SEQUENCE dbo.mytable_seq RESTART WITH 1;
INSERT INTO dbo.mytable DEFAULT VALUES;
INSERT INTO dbo.mytable DEFAULT VALUES;
SELECT * FROM dbo.mytable;
GO
ROLLBACK;
GO
INSERT INTO dbo.mytable DEFAULT VALUES;
INSERT INTO dbo.mytable DEFAULT VALUES;
SELECT * FROM dbo.mytable;
В этом случае ваш тест будет выглядеть так:
CREATE PROCEDURE mytests.[test inserts rows using NEXT VALUE FOR dbo.mytable_seq]
AS
BEGIN
ALTER SEQUENCE dbo.mytable_seq RESTART WITH 1;
EXEC tSQLt.FakeTable 'dbo.mytable', @Defaults = 0; --make sure the proc doesn't just rely on the constraint.
EXEC dbo.myproc;
SELECT id FROM dbo.mytable INTO #Actual;
SELECT TOP(0) * FROM #Actual INTO #Expected;
INSERT INTO #Expected VALUES(1),(2);
EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
END;
В моем ограниченном тестировании откат вроде бы все вернул на место, но, согласно документации, в последовательности могут возникнуть пробелы. Кроме того, если ROLLBACK
выйдет из строя, вернуть систему в исходное состояние будет сложнее.
При этом вам не следует запускать тесты в среде, содержащей важные для вас данные. Если вы последуете этому совету, я бы пошел более простым путем и выбрал вариант 2.