TSQLt — Как подделывать/издеваться над последовательностями

Я создаю различные модульные тесты для нашей базы данных, используя платформу tSQLt. Многие из наших хранимых процедур вставляют записи в различные таблицы. Эти процедуры напрямую вызывают наши последовательности для создания идентификаторов вставляемых записей:

SET tID = NEXT VALUE dbo.TableName_SEQ

При создании модульного теста для процедур, ссылающихся на эту последовательность, я попытался использовать tSQLt.RemoveObject, но получил ошибку:

Объект «[dbo].[TableName_SEQ]» невозможно переименовать, поскольку объект участвует в принудительных зависимостях.

Поскольку процедура, которую я тестирую, напрямую использует эту последовательность, как мне с этим справиться? Существует ли какой-либо тип объекта tSQLt, который может подделать последовательность, например «FakeFunction», и вводить желаемый идентификатор каждый раз при ее запуске?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

На данный момент этого нет. Это та же проблема, что и с индексированными представлениями, которые также используют привязку схемы. Я начал заниматься этим автоматически, но расчетного времени прибытия нет.

На данный момент у вас есть два варианта. Один из них — вручную разорвать привязку схемы. Вы можете сделать это с помощью этого оператора:

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.

Другие вопросы по теме