У кого-нибудь был успех в модульном тестировании хранимых процедур SQL?

Для PHP, «всегда безопасно предшествовать не-буквенно-цифровому с« \ », чтобы указать, что он обозначает себя». - http://php.net/manual/en/regexp.reference.escape.php .

За исключением случаев, когда это «или».: /

Чтобы избежать переменных шаблона регулярных выражений (или частичных переменных) в PHP, используйте preg_quote ()

37
задан Chris 23 August 2008 в 19:43
поделиться

16 ответов

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

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

Вот отрывок абстрактного базового класса, используемого для доступа к данным

Public MustInherit Class Repository(Of T As Class)
    Implements IRepository(Of T)

    Private mConnectionString As String = ConfigurationManager.ConnectionStrings("Northwind.ConnectionString").ConnectionString
    Private mConnection As IDbConnection
    Private mTransaction As IDbTransaction

    Public Sub New()
        mConnection = Nothing
        mTransaction = Nothing
    End Sub

    Public Sub New(ByVal connection As IDbConnection, ByVal transaction As IDbTransaction)
        mConnection = connection
        mTransaction = transaction
    End Sub

    Public MustOverride Function BuildEntity(ByVal cmd As SqlCommand) As List(Of T)

    Public Function ExecuteReader(ByVal Parameter As Parameter) As List(Of T) Implements IRepository(Of T).ExecuteReader
        Dim entityList As List(Of T)
        If Not mConnection Is Nothing Then
            Using cmd As SqlCommand = mConnection.CreateCommand()
                cmd.Transaction = mTransaction
                cmd.CommandType = Parameter.Type
                cmd.CommandText = Parameter.Text
                If Not Parameter.Items Is Nothing Then
                    For Each param As SqlParameter In Parameter.Items
                        cmd.Parameters.Add(param)
                    Next
                End If
                entityList = BuildEntity(cmd)
                If Not entityList Is Nothing Then
                    Return entityList
                End If
            End Using
        Else
            Using conn As SqlConnection = New SqlConnection(mConnectionString)
                Using cmd As SqlCommand = conn.CreateCommand()
                    cmd.CommandType = Parameter.Type
                    cmd.CommandText = Parameter.Text
                    If Not Parameter.Items Is Nothing Then
                        For Each param As SqlParameter In Parameter.Items
                            cmd.Parameters.Add(param)
                        Next
                    End If
                    conn.Open()
                    entityList = BuildEntity(cmd)
                    If Not entityList Is Nothing Then
                        Return entityList
                    End If
                End Using
            End Using
        End If

        Return Nothing
    End Function
End Class

следующий, Вы будете видеть, что демонстрационный класс доступа к данным использует вышеупомянутую основу для получения списка продуктов

Public Class ProductRepository
    Inherits Repository(Of Product)
    Implements IProductRepository

    Private mCache As IHttpCache

    'This const is what you will use in your app
    Public Sub New(ByVal cache As IHttpCache)
        MyBase.New()
        mCache = cache
    End Sub

    'This const is only used for testing so we can inject a connectin/transaction and have them roll'd back after the test
    Public Sub New(ByVal cache As IHttpCache, ByVal connection As IDbConnection, ByVal transaction As IDbTransaction)
        MyBase.New(connection, transaction)
        mCache = cache
    End Sub

    Public Function GetProducts() As System.Collections.Generic.List(Of Product) Implements IProductRepository.GetProducts
        Dim Parameter As New Parameter()
        Parameter.Type = CommandType.StoredProcedure
        Parameter.Text = "spGetProducts"
        Dim productList As List(Of Product)
        productList = MyBase.ExecuteReader(Parameter)
        Return productList
    End Function

    'This function is used in each class that inherits from the base data access class so we can keep all the boring left-right mapping code in 1 place per object
    Public Overrides Function BuildEntity(ByVal cmd As System.Data.SqlClient.SqlCommand) As System.Collections.Generic.List(Of Product)
        Dim productList As New List(Of Product)
        Using reader As SqlDataReader = cmd.ExecuteReader()
            Dim product As Product
            While reader.Read()
                product = New Product()
                product.ID = reader("ProductID")
                product.SupplierID = reader("SupplierID")
                product.CategoryID = reader("CategoryID")
                product.ProductName = reader("ProductName")
                product.QuantityPerUnit = reader("QuantityPerUnit")
                product.UnitPrice = reader("UnitPrice")
                product.UnitsInStock = reader("UnitsInStock")
                product.UnitsOnOrder = reader("UnitsOnOrder")
                product.ReorderLevel = reader("ReorderLevel")
                productList.Add(product)
            End While
            If productList.Count > 0 Then
                Return productList
            End If
        End Using
        Return Nothing
    End Function
End Class

, И теперь в модульном тесте можно также наследоваться очень простому базовому классу, который делает установку / работа отката - или сохраняет, это на на основание модульного теста

ниже является простым базовым классом тестирования, который я использовал

Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Imports Microsoft.VisualStudio.TestTools.UnitTesting

Public MustInherit Class TransactionFixture
    Protected mConnection As IDbConnection
    Protected mTransaction As IDbTransaction
    Private mConnectionString As String = ConfigurationManager.ConnectionStrings("Northwind.ConnectionString").ConnectionString

    <TestInitialize()> _
    Public Sub CreateConnectionAndBeginTran()
        mConnection = New SqlConnection(mConnectionString)
        mConnection.Open()
        mTransaction = mConnection.BeginTransaction()
    End Sub

    <TestCleanup()> _
    Public Sub RollbackTranAndCloseConnection()
        mTransaction.Rollback()
        mTransaction.Dispose()
        mConnection.Close()
        mConnection.Dispose()
    End Sub
End Class

, и наконец - ниже простой тест с помощью того тестового базового класса, который показывает, как протестировать весь цикл CRUD, чтобы удостовериться, что все sprocs делают свое задание и что код ado.net делает лево-право, отображающееся правильно

, я знаю, что это не тестирует "spGetProducts" sproc, используемый в вышеупомянутом образце доступа к данным, но необходимо видеть питание позади этого подхода к поблочному тестированию sprocs

Imports SampleApplication.Library
Imports System.Collections.Generic
Imports Microsoft.VisualStudio.TestTools.UnitTesting

<TestClass()> _
Public Class ProductRepositoryUnitTest
    Inherits TransactionFixture

    Private mRepository As ProductRepository

    <TestMethod()> _
    Public Sub Should-Insert-Update-And-Delete-Product()
        mRepository = New ProductRepository(New HttpCache(), mConnection, mTransaction)
        '** Create a test product to manipulate throughout **'
        Dim Product As New Product()
        Product.ProductName = "TestProduct"
        Product.SupplierID = 1
        Product.CategoryID = 2
        Product.QuantityPerUnit = "10 boxes of stuff"
        Product.UnitPrice = 14.95
        Product.UnitsInStock = 22
        Product.UnitsOnOrder = 19
        Product.ReorderLevel = 12
        '** Insert the new product object into SQL using your insert sproc **'
        mRepository.InsertProduct(Product)
        '** Select the product object that was just inserted and verify it does exist **'
        '** Using your GetProductById sproc **'
        Dim Product2 As Product = mRepository.GetProduct(Product.ID)
        Assert.AreEqual("TestProduct", Product2.ProductName)
        Assert.AreEqual(1, Product2.SupplierID)
        Assert.AreEqual(2, Product2.CategoryID)
        Assert.AreEqual("10 boxes of stuff", Product2.QuantityPerUnit)
        Assert.AreEqual(14.95, Product2.UnitPrice)
        Assert.AreEqual(22, Product2.UnitsInStock)
        Assert.AreEqual(19, Product2.UnitsOnOrder)
        Assert.AreEqual(12, Product2.ReorderLevel)
        '** Update the product object **'
        Product2.ProductName = "UpdatedTestProduct"
        Product2.SupplierID = 2
        Product2.CategoryID = 1
        Product2.QuantityPerUnit = "a box of stuff"
        Product2.UnitPrice = 16.95
        Product2.UnitsInStock = 10
        Product2.UnitsOnOrder = 20
        Product2.ReorderLevel = 8
        mRepository.UpdateProduct(Product2) '**using your update sproc
        '** Select the product object that was just updated to verify it completed **'
        Dim Product3 As Product = mRepository.GetProduct(Product2.ID)
        Assert.AreEqual("UpdatedTestProduct", Product2.ProductName)
        Assert.AreEqual(2, Product2.SupplierID)
        Assert.AreEqual(1, Product2.CategoryID)
        Assert.AreEqual("a box of stuff", Product2.QuantityPerUnit)
        Assert.AreEqual(16.95, Product2.UnitPrice)
        Assert.AreEqual(10, Product2.UnitsInStock)
        Assert.AreEqual(20, Product2.UnitsOnOrder)
        Assert.AreEqual(8, Product2.ReorderLevel)
        '** Delete the product and verify it does not exist **'
        mRepository.DeleteProduct(Product3.ID)
        '** The above will use your delete product by id sproc **'
        Dim Product4 As Product = mRepository.GetProduct(Product3.ID)
        Assert.AreEqual(Nothing, Product4)
    End Sub

End Class

, я знаю, что это - долгий пример, но это помогло иметь допускающий повторное использование класс для работы доступа к данным и все же другой допускающий повторное использование класс для моего тестирования, таким образом, я не должен был делать работы установки/разрушения много раз;)

12
ответ дан Toran Billups 23 August 2008 в 19:43
поделиться

Проблема с поблочным тестированием, которое любой вид связанного с данными программирования - то, что у Вас должен быть надежный набор данных тестирования для запуска с. Много также зависит от сложности сохраненного proc и что это делает. Было бы очень трудно автоматизировать поблочное тестирование на очень сложную процедуру, которая изменила много таблиц.

Некоторые из других плакатов отметили некоторые простые способы автоматизировать вручную тестирование их, и также некоторых инструментов, которые можно использовать с SQL Server. На стороне Oracle МН гуру / гуру SQL Steven Feuerstein работали над свободным инструментом поблочного тестирования для МН хранимых процедур / хранимых процедур SQL, названных utPLSQL.

Однако он отбросил то усилие и затем пошел коммерческий с Тестером Кода Quest для МН / SQL. Quest предлагает бесплатную загружаемую пробную версию. Я нахожусь на грани испытания его; мое понимание - то, что это способно заботиться об издержках в установке среды тестирования так, чтобы можно было сфокусироваться на просто тестах самих, и это сохраняет тесты, таким образом, можно снова использовать их в регрессионном тестировании, одном из больших преимуществ разработки через тестирование. Кроме того, это, как предполагается, способно к больше, чем просто проверка выходной переменной и действительно имеет условие для проверки изменений данных, но я все еще должен более тщательно изучить сам. Я думал, что эта информация могла бы быть значимой для пользователей Oracle.

0
ответ дан Bernard Dy 23 August 2008 в 19:43
поделиться

О, мальчик. sprocs не предоставляют себя (автоматизированному) поблочному тестированию. Я сортирую - "модульного теста" мой комплекс sprocs путем записи тестов в t-sql пакетных файлах и руке, проверяющей вывод операторов печати и результатов.

0
ответ дан Booji Boy 23 August 2008 в 19:43
поделиться

Одна опция осуществить рефакторинг код (я допущу ужасный взлом) состояла бы в том, чтобы генерировать его через CPP (препроцессор C), M4 (никогда не пробовал его), и т.п. У меня есть проект, который делает просто это, и это на самом деле главным образом осуществимо.

единственный случай я думаю, что это могло бы быть допустимо для, 1) как альтернатива KLOC + хранимые процедуры и 2) и это - мои случаи, когда точка проекта должна видеть, как далеко (в безумный) можно продвинуть технологию.

0
ответ дан BCS 23 August 2008 в 19:43
поделиться

Мы модульный тест код C#, который называет SPS
, у Нас есть сценарии сборки, создавая чистые тестовые базы данных.
И большие мы присоединяем и отсоединяемся во время тестового приспособления.
Эти тесты могли занять часы, но я думаю, что это стоит того.

0
ответ дан John Smithers 23 August 2008 в 19:43
поделиться

Мы используем , DataFresh для откатывания изменений между каждым тестом, затем тестируя sprocs относительно легок.

то, Чему все еще недостает, является инструментами покрытия кода.

1
ответ дан Keith 23 August 2008 в 19:43
поделиться

Я предполагаю, что Вы хотите поблочное тестирование в MSSQL. Рассмотрение DBUnit существует некоторые ограничения в, он - поддержка MSSQL. Это не поддерживает NVarChar, например. Вот некоторые реальные пользователи и их проблемы с DBUnit.

4
ответ дан RedWolves 23 August 2008 в 19:43
поделиться
  • 1
    Интересно, практичен ли user-without-GPU-case действительно настолько. Вместо того, чтобы поддержать код нейтрализации OpenMP, you' d должны поддержать код нейтрализации OpenCL как центральные процессоры won' t поддерживают 2D локальные размеры работы, имеют проблемы с __ локальная память и этажерка. Не очень полученный там, если Вы оптимизировали ядра GPU. – w-m 31 August 2011 в 22:22

Если Вы думаете о виде кода, что поблочное тестирование имеет тенденцию продвигать: маленькие высоко связные и непритязательно связанные стандартные программы, тогда необходимо в значительной степени быть в состоянии видеть, где, по крайней мере, часть проблемы могла бы быть.

В моем циничном мире, хранимые процедуры являются частью RDBMS давняя попытка в мире убедить Вас переместить свою обработку бизнеса в базу данных, которая имеет смысл, когда Вы полагаете, что стоимость лицензии сервера имеет тенденцию быть связанной с вещами как количество процессора. Чем больше материала Вы выполняете в своей базе данных, тем больше они делают от Вас.

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

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

Что-то как этот.

6
ответ дан Mike Woodhouse 23 August 2008 в 19:43
поделиться
  • 1
    @user57368: Просто дополнение, что использование оптимизации, такой как явное использование локальной памяти имеет смысл для GPU. На центральных процессорах, имея это оптимизация " май negatively" влияйте на производительность, по крайней мере при использовании реализации Intel OpenCL для x86 центральных процессоров. – usman 1 September 2011 в 11:40

Вы попробовали DBUnit? Это разработано к модульному тесту Ваша база данных, и просто Ваша база данных, не будучи должен пройти Ваш код C#.

10
ответ дан Jon Limjap 23 August 2008 в 19:43
поделиться

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

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

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

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

Однако мы теперь принимаем модель веб-сервисов для наших новых возможностей, и мы старались избегать хранимых процедур как можно больше, сохраняя логику в коде C# и запуская SQLCommands в базу данных (хотя linq теперь был бы предпочтительным методом). Существует все еще некоторое использование существующего SPS, который был, почему я думал о ретроспективно поблочном тестировании их.

2
ответ дан John Sibly 23 August 2008 в 19:43
поделиться
  • 1
    @user57368: Возможно, Intel SDK прокладывает себе путь. Apple' s doesn' t. CL_DEVICE_MAX_WORK_ITEM_SIZES для моего Core2Duo в соответствии с Mac OS 10.6 был {1,1,1}, под 10,7 это {по крайней мере 1024,1,1}, но все еще не 2D. Кроме того, любое ядро больше чем с одной локальной переменной заставило бы компилятор сдаться под 10,6 - я назвал бы то повреждение кода. – w-m 1 September 2011 в 12:43

LINQ упростит это, только если Вы удаляете логику из своих хранимых процедур и повторно реализуете ее как linq запросы. Который был бы намного более устойчивым и легче протестировать, определенно. Однако это кажется, что Ваши требования устранили бы это.

TL; DR: Ваш дизайн имеет проблемы.

0
ответ дан Will 23 August 2008 в 19:43
поделиться
  • 1
    Мерцание понимания, позвольте мне пойти, пробуют его:) – Thufir 30 July 2012 в 09:33

Можно также попробовать Visual Studio за Профессионалов Базы данных . Это главным образом об управлении изменениями, но также и имеет инструменты для генерации данных тестирования и модульных тестов.

Это - довольно дорогой tho.

2
ответ дан Craig 23 August 2008 в 19:43
поделиться

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

я должен вставить свой голос за то, что в большой степени одобрили хранимые процедуры и поместили Вашу бизнес-логику, где я (и большая часть DBAs) думаю, что это принадлежит в базе данных.

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

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

6
ответ дан Eric Z Beard 23 August 2008 в 19:43
поделиться
  • 1
    Почему делают Вы думаете что основанные на ЦП реализации can' t поддерживают 2D локальные размеры рабочей группы или локальную память? На ЦП кэш-памятью управляют аппаратные средства вместо программного обеспечения, таким образом, единственная разница между глобальной и локальной памятью была бы, необходима ли блокировка для доступа к нему. Размеры рабочей группы составили бы подсказки планировщика для систем NUMA. Да, большое усилие по оптимизации помещало в код OpenCL, чтобы заставить его работать хорошо на GPU won' t влияют на производительность на ЦП, но этом won' t повреждают код, также. Любое ядро, которое будет работать на GPU, может работать на совместимой реализации ЦП. – user57368 1 September 2011 в 01:16

Я нахожусь в той же самой ситуации как исходный плакат. Это сводится к производительности по сравнению с тестируемостью. Моя предвзятость находится к тестируемости (заставьте его работать, сделайте его правом, сделайте его быстро), который предлагает не допустить бизнес-логику в базу данных. Базы данных не только испытывают недостаток в средах тестирования, конструкциях факторинга кода, и анализе кода и инструментах навигации, найденных на языках как Java, но высоко учтенный код базы данных является также медленным (где высоко учтенный код Java не).

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

На связанной ноте, кажется, что очень длинный и процедурный код базы данных часто является признаком чего-то еще, и я думаю, что такой код может быть преобразован в тестируемый код, не подвергаясь хиту производительности. Теория состоит в том, что такой код часто представляет пакетные обработки, которые периодически обрабатывают большие объемы данных. Если бы эти пакетные обработки должны были быть преобразованы в меньшие блоки бизнес-логики в реальном времени, которая работает каждый раз, когда входные данные изменяются, эта логика могла быть выполнена на среднем уровне (где это может быть протестировано), не получая удар производительности (так как работа сделана в маленьких блоках в режиме реального времени). Как побочный эффект, это также устраняет длинную обратную связь обработки ошибок пакетной обработки. Конечно, этот подход не будет работать во всех случаях, но он может работать в некоторых. Кроме того, если существуют тонны такого непригодного для тестирования кода базы данных пакетной обработки в Вашей системе, дорога к спасению может быть длинной и трудной. YMMV.

3
ответ дан thvo 23 August 2008 в 19:43
поделиться

Хороший вопрос.

у меня есть подобные проблемы, и я взял путь наименьшего сопротивления (для меня, так или иначе).

существует набор других решений, которые упомянули другие. Многие из них лучше / более чистый / более подходящий для других.

я уже использовал Делавший пробную поездку. NET/MbUnit для тестирования моего C#, таким образом, я просто добавил тесты к каждому проекту назвать хранимые процедуры используемыми тем приложением.

я знаю, я знаю. Это звучит ужасным, но в чем я нуждаюсь, должен успешно стартовать с [приблизительно 110] тестирование и пойти оттуда. Этот подход означает, что, хотя мое покрытие является низким, я тестирую, некоторые сохранили procs в то же время, что и я тестирую код, который будет называть их. Существует некоторая логика к этому.

4
ответ дан AJ. 23 August 2008 в 19:43
поделиться

I do poor man's unit testing. If I'm lazy, the test is just a couple of valid invocations with potentially problematic parameter values.

/*

--setup
Declare @foo int Set @foo = (Select top 1 foo from mytable)

--test
execute wish_I_had_more_Tests @foo

--look at rowcounts/look for errors
If @@rowcount=1 Print 'Ok!' Else Print 'Nokay!'

--Teardown
Delete from mytable where foo = @foo
*/
create procedure wish_I_had_more_Tests
as
select....
1
ответ дан 27 November 2019 в 04:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: