Каков наилучший способ тестирования хранимой процедуры?

#sql-server

Вопрос:

Как и во многих компаниях, которым требуется, чтобы весь доступ осуществлялся через хранимые процедуры, у нас, похоже, много бизнес-логики заперто в sprocs. Эти вещи просто трудно проверить, и некоторые из них уже давно стали глупыми. Есть ли у кого-нибудь набор лучших практик, которые могут немного облегчить уверенное тестирование этих вещей?

В настоящее время мы поддерживаем около 30 «проблемных» баз данных, с которыми мы сталкиваемся. Это не всегда особенно хорошо документировано и, безусловно, не автоматизировано.

Ответ №1:

Коллега клянется платформой тестирования TSQLUnit. Возможно, стоит обратить внимание на ваши потребности.

Ответ №2:

У нас был очень тонкий уровень доступа к данным, который в основном состоял из хранимых процедур, похожих на методы C#. Затем наш набор тестов NUnit был настроен/демонтирован для создания/отката транзакции и методов тестирования, которые вызывались в DAL. Ничего особенного, и оказалось, что его проще поддерживать, чем набор тестов TSQLUnit.

Комментарии:

1. Этот метод выглядит простым в настройке, но запускаете ли вы sprocs последовательно? Это может вызвать некоторые проблемы с их параллельным запуском.

Ответ №3:

Не уверен, что это то, что вы ищете, но поскольку вы используете SQL Server: я обнаружил, что LINQ-отличный инструмент для тестирования хранимых процессов. Вы можете просто перетащить хранимые процедуры на диаграмму DBML, а затем вызвать их в качестве методов в своем datacontext. Лучше, чем настройка соединений ADO и т. Д. Для тестовой проводки. Например, если вы настроили тестовый проект в Visual Studio, вы можете просто протестировать свои процедуры, такие как методы, на другом объекте. Если ваши сохраненные процедуры возвращают результирующие наборы, я думаю, что LINQ переведет это в анонимные переменные, к которым вы сможете получить доступ через IEnumerable или IQueryable (кто-нибудь, пожалуйста, проверьте это). Но если вы возвращаете только коды возврата, это должен быть быстрый и довольно простой способ.

Комментарии:

1. Читатели: Обратите внимание, что это относится к LINQ to SQL.

Ответ №4:

Я заметил, что ваш пост был помечен как SQLServer. Если это так, то вам следует ознакомиться с выпуском Team для специалистов по базам данных, который является частью Visual Studio. Вот несколько статей:

Последний на самом деле является кросс-БД платформой, в то время как DBPro на данный момент является исключительно SQL Server.

Комментарии:

1. ссылка на MSDN magazine article это по состоянию на июнь 2021 года.

Ответ №5:

Один из методов, который я использовал, — это написать «временный» модульный тест для рефакторинга определенной хранимой процедуры. Вы сохраняете данные из набора запросов из базы данных и сохраняете их там, где модульный тест может их получить.

Затем произведите рефакторинг вашего программного обеспечения. Возвращаемые данные должны быть одинаковыми и могут быть сопоставлены непосредственно с сохраненными данными автоматически или вручную.

Альтернативой является параллельный запуск двух хранимых процедур и сравнение наборов результатов.

Это особенно хорошо работает для хранимых процедур только для выбора, но обновления, вставки и удаления являются более сложными.

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

Ответ №6:

Попробуйте TST. Вы можете скачать и установить его по адресу: http://tst.codeplex.com/

Ответ №7:

Это кажется ужасной политикой. Возможно, вы можете написать хранимую процедуру, которая выполняет SQL, и начать переводить свой код для выполнения там.

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

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

Комментарии:

1. Политика существует, потому что в свое время весь SQL был построен и выполнен на среднем уровне, а иногда и на переднем. Как и во многих других местах, люди иногда доводят коррекцию до крайности. Вы правы в том, что вопросы становятся слишком длинными, чтобы сделать хороший рефакторинг, хотя мне нужны тесты.

Ответ №8:

Вот мой низкотехнологичный, быстрый способ просто хранить примеры входных данных, удобно расположенных в DDL

 
USE [SpacelySprockets]

GO

/****** Object:  StoredProcedure [dbo].[uspBrownNoseMrSpacely]    Script Date: 02/03/3000 00:24:41 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

--================================

--Stored Procedure DDL:

--================================

--Example Inputs

/*

DECLARE @SuckupPloyId              int

DECLARE @SuckupIdentityRecordId        int 

SET        @SuckupPloyId               = 3

*/

-- =============================================

-- Author:     6eorge Jetson

-- Create date: 01/02/3000

-- Description:    Sucks up to the boss

-- =============================================

CREATE PROCEDURE [dbo].[uspBrownNoseMrSpacely]

    @SuckupPloyId              int

   ,@SuckupIdentityRecordId    int OUTPUT 

AS

BEGIN
    DECLARE @EmployeeId     int
    DECLARE @SuckupPoints   int
    DECLARE @DateTimeStamp  datetime

    SET     @EmployeeId     = dbo.svfGetEmployeeId('6eorge Jetson')
    SET     @SuckupPoints   = dbo.svfGetSuckupPoints(@SuckupPloyId)
    SET     @DateTimeStamp  = getdate()

    --Data state-changing statement in sproc
    INSERT INTO [dbo].[tblSuckupPointsEarned]([EmployeeId], [SuckupPoints], [DateTimeStamp] )
    VALUES (@EmployeeId, @SuckupPoints, @DateTimeStamp)

    SET @SuckupIdentityRecordId = @@Identity
END
  
--Отображение Доказательств Модульного Тестирования
/*
выбирать
 @EmployeeID как EmployeeID
,@SuckupPoints как SuckupPoints
 ,@Метка времени как метка времени
*/


--==========================================================================
--После редактирования для низкотехнологичного, негосударственного изменения "модульного" вызова теста
--==========================================================================

--Примеры Входных данных
ОБЪЯВИТЕ @SuckupPloyId int
ОБЪЯВИТЕ @SuckupIdentityRecordId int 
УСТАНОВИТЬ @SuckupPloyId = 3

/*
-- =============================================
-- Автор: 6еордж Джетсон
-- Дата создания: 01/02/3000
-- Описание: Подлизывается к боссу
-- =============================================
СОЗДАЙТЕ ПРОЦЕДУРУ [dbo].[uspBrownNoseMrSpacely]
 @SuckupPloyId int
,@SuckupIdentityRecordId int ВЫВОД 
КАК
НАЧАТЬ
*/
 ОБЪЯВИТЬ @EmployeeID int
ОБЪЯВИТЬ @SuckupPoints int
ОБЪЯВИТЬ @DateTimeStamp дата и время

 УСТАНОВИТЕ @EmployeeID = dbo.svfGetEmployeeId('6eorge Jetson')
 УСТАНОВИТЕ @SuckupPoints = dbo.svfGetSuckupPoints(@SuckupPloyId)
 УСТАНОВИТЕ @DateTimeStamp = getdate()

 --Заявление об изменении состояния данных теперь закомментировано, чтобы предотвратить изменение состояния данных
-- ВСТАВИТЬ В [dbo].[tblSuckupPointsEarned]([EmployeeID], [SuckupPoints], [Метка времени] )
-- ЗНАЧЕНИЯ (@EmployeeID, @SuckupPoints, @Отметка времени)

 УСТАНОВИТЕ @SuckupIdentityRecordId = @@Идентификатор

--КОНЕЦ-Нужно также прокомментировать вопрос "КОНЕЦ" 

--Отображение Доказательств Модульного Тестирования
выбирать
 @EmployeeID как EmployeeID
,@SuckupPoints как SuckupPoints
 ,@Метка времени как метка времени


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

Утверждайте, что мой sproc управляемого размера проходит, по крайней мере, простой «модульный тест».

перед выполнением ПРОЦЕДУРЫ СОЗДАНИЯ я обнаружил, что совершаю меньше ошибок (вероятно, из-за большей дисциплины, чем сам тест).