Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Есть ли у кого-нибудь успехи в модульном тестировании SQL хранимых процедур?
Мы обнаружили, что модульные тесты, которые мы написали для нашего кода C#/C++, действительно окупились. Но у нас все еще есть тысячи линий бизнес-логики в хранимых процедурах, которые действительно тестируются в гневе, когда наш продукт развертывается для большого числа пользователей.
Хуже всего то, что некоторые из этих хранимых процедур оказываются очень длинными из-за снижения производительности при передаче временных таблиц между SPs. Это помешало нам провести рефакторинг для упрощения кода.
Мы предприняли несколько попыток построить модульные тесты вокруг некоторых наших ключевых хранимых процедур (в первую очередь тестирования производительности), но обнаружили, что настройка тестовых данных для этих тестов действительно трудна. Например, мы в конечном итоге копируем вокруг тестовых баз данных. В дополнение к этому, тесты в конечном итоге оказываются действительно чувствительными к изменениям, и даже самым маленьким изменениям в сохраненном проке. или таблица требует большого количества изменений в тестах. Поэтому после того, как многие сборки ломаются из-за того, что эти тесты базы данных периодически проваливаются, нам просто нужно было вытащить их из процесса сборки.
Итак, основная часть моих вопросов заключается в следующем: кто-нибудь когда-нибудь успешно писал модульные тесты для своих хранимых процедур?
Вторая часть моих вопросов заключается в том, будет ли модульное тестирование проще/легче с linq?
Я подумал, что вместо того, чтобы создавать таблицы тестовых данных, вы можете просто создать коллекцию тестовых объектов и протестировать свой код linq в ситуации “linq to objects”? (Я совершенно новичок в linq, так что не знаю, сработает ли это вообще)
Я столкнулся с этой же проблемой некоторое время назад и обнаружил, что если бы я создал простой абстрактный базовый класс для доступа к данным, который позволял бы мне вводить соединение и транзакцию, я мог бы юнит-тестировать свои sprocs, чтобы увидеть, выполнили ли они работу в SQL, которую я попросил их сделать, а затем откатить, чтобы ни один из тестовых данных не остался в БД.
Это было лучше, чем обычный "запуск скрипта для настройки моей тестовой БД, а затем после запуска тестов выполните очистку от мусора/тестовых данных". Это также чувствовалось ближе к модульному тестированию, потому что эти тесты можно было бы запускать в одиночку без большого количества "все в БД должно быть 'just so', прежде чем я выполню эти тесты".
Вот фрагмент абстрактного базового класса, используемого для доступа к данным
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, используемый в приведенном выше примере доступа к данным, но вы должны увидеть силу этого подхода к модульному тестированию sproc
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
Я знаю, что это длинный пример, но он помог иметь многоразовый класс для работы с доступом к данным и еще один многоразовый класс для моего тестирования, поэтому мне не нужно было снова и снова выполнять установку/демонтаж ;)
Если вы подумаете о типе кода, который модульное тестирование обычно продвигает: небольшие высокосвязанные и слабо связанные подпрограммы, то вы должны в значительной степени быть в состоянии увидеть, где может быть хотя бы часть проблемы.
В моем циничном мире хранимые процедуры являются частью давней попытки мира RDBMS убедить вас перенести обработку вашего бизнеса в базу данных, что имеет смысл, если учесть, что стоимость лицензии сервера, как правило, связана с такими вещами, как количество процессоров. Чем больше материала вы запускаете в своей базе данных, тем больше они делают из вас.
Но у меня сложилось впечатление, что вы на самом деле больше озабочены производительностью, которая на самом деле вовсе не является прерогативой юнит-тестирования. Модульные тесты должны быть достаточно атомарными и предназначены для проверки поведения, а не производительности. И в этом случае вам почти наверняка понадобятся нагрузки производственного класса, чтобы проверить планы запросов.
Я думаю, что вам нужен другой класс среды тестирования. Я бы предложил копию производства как самую простую, предполагая, что безопасность не является проблемой. Затем для каждого выпуска-кандидата вы начинаете с предыдущей версии, мигрируете с помощью своих процедур выпуска (что даст им хорошее тестирование в качестве побочного эффекта) и запускаете свои тайминги.
Что-то вроде того.
Ключом к тестированию хранимых процедур является написание сценария, который заполняет пустую базу данных данными, заранее запланированными для обеспечения согласованного поведения при вызове хранимых процедур.
Я должен отдать свой голос за то, чтобы сильно поддержать хранимые процедуры и поместить вашу бизнес-логику туда, где я (и большинство DBAs) думаю, что она принадлежит, в базу данных.
Я знаю, что мы, инженеры-программисты, хотим, чтобы красиво переработанный код, написанный на нашем любимом языке, содержал всю нашу важную логику, но реалии производительности в системах большого объема и критическая природа целостности данных требуют от нас некоторых компромиссов. Sql код может быть уродливым, повторяющимся и трудным для тестирования, но я не могу представить себе трудности настройки базы данных без полного контроля над дизайном запросов.
Я часто вынужден полностью переделывать запросы, включать изменения в модель данных, чтобы заставить вещи работать в приемлемое время. С помощью хранимых процедур я могу гарантировать, что изменения будут прозрачны для вызывающего объекта, поскольку хранимая процедура обеспечивает такую превосходную инкапсуляцию.
Я предполагаю, что вы хотите провести модульное тестирование в MSSQL. Глядя на DBUnit, есть некоторые ограничения в его поддержке для MSSQL. Например, он не поддерживает NVarChar. Вот некоторые реальные пользователи и их проблемы с DBUnit.
Лучший вопрос.
У меня есть похожие проблемы, и я пошел по пути наименьшего сопротивления (во всяком случае, для меня).
Есть куча других решений, о которых уже упоминали другие. Многие из них лучше / более чистые / более подходящие для других.
Я уже использовал Testdriven.NET/MbUnit для тестирования своего C#,, поэтому я просто добавил тесты в каждый проект, чтобы вызвать хранимые процедуры, используемые этим приложением.
Я знаю, я знаю. Это звучит ужасно, но что мне нужно, так это оторваться от Земли с некоторыми испытаниями и идти оттуда. Этот подход означает, что, хотя мой охват невелик, я тестирую некоторые сохраненные процессы одновременно с тестированием кода, который будет их вызывать. В этом есть определенная логика.
Я нахожусь в той же самой ситуации, что и оригинальный плакат. Все сводится к производительности и тестируемости. Я склоняюсь к тестируемости (сделать его работоспособным, сделать его правильным, сделать его быстрым), что предполагает сохранение бизнес-логики вне базы данных. В базах данных не только отсутствуют тестовые платформы, конструкции факторинга кода, а также инструменты анализа кода и навигации, найденные в таких языках, как Java, но и высокофакторизованный код базы данных также медленен (там, где высокофакторизованный код Java отсутствует).
Тем не менее, я признаю силу обработки набора баз данных. При правильном использовании SQL может делать некоторые невероятно мощные вещи с очень небольшим количеством кода. Итак, я согласен с некоторой логикой, основанной на множестве, живущей в базе данных, хотя я все равно сделаю все возможное, чтобы ее проверить.
В связи с этим, похоже, что очень длинный и процедурный код базы данных часто является симптомом чего-то другого, и я думаю, что такой код можно преобразовать в тестируемый код без ущерба для производительности. Теория заключается в том, что такой код часто представляет собой пакетные процессы, которые периодически обрабатывают большие объемы данных. Если бы эти пакетные процессы были преобразованы в более мелкие фрагменты бизнес-логики реального времени, которая выполняется всякий раз, когда входные данные изменяются, эта логика могла бы выполняться на среднем уровне (где она может быть протестирована) без снижения производительности (поскольку работа выполняется небольшими фрагментами в реальном времени). В качестве побочного эффекта это также устраняет длинные обратные циклы обработки ошибок пакетного процесса. Конечно, этот подход не будет работать во всех случаях, но он может сработать в некоторых. Кроме того, если в вашей системе есть тонны такого непроверяемого кода базы данных пакетной обработки, путь к спасению может быть долгим и трудным. YMMV.
Вы также можете попробовать Visual Studio для специалистов по базам данных . Это в основном управление изменениями, но также есть инструменты для генерации тестовых данных и модульных тестов.
Это довольно дорого, Тхо.
Но у меня сложилось впечатление, что вы на самом деле больше озабочены производительностью, которая на самом деле вовсе не является прерогативой юнит-тестирования. Модульные тесты должны быть достаточно атомарными и предназначены для проверки поведения, а не производительности. И в этом случае вам почти наверняка понадобятся нагрузки производственного класса, чтобы проверить планы запросов.
Но у меня сложилось впечатление, что вы на самом деле больше озабочены производительностью, которая на самом деле вовсе не является прерогативой юнит-тестирования. Модульные тесты должны быть достаточно атомарными и предназначены для проверки поведения, а не производительности. И в этом случае вам почти наверняка понадобятся нагрузки производственного класса, чтобы проверить планы запросов.
Я думаю, что здесь есть две совершенно разные области тестирования: производительность и фактическая логика хранимых процедур.
Я привел пример тестирования производительности БД в прошлом, и, к счастью, мы достигли точки, где производительность достаточно хороша.
Я полностью согласен с тем, что ситуация со всей бизнес-логикой в базе данных является плохой, но это то, что мы унаследовали от большинства наших разработчиков, прежде чем они присоединились к компании.
Однако теперь мы используем модель веб-служб для наших новых функций, и мы стараемся максимально избегать хранимых процедур, сохраняя логику в коде C# и запуская SQLCommands в базе данных (хотя теперь предпочтительным методом будет linq). Существует еще некоторое использование существующего SPs, поэтому я думал о ретроспективном юнит-тестировании их.
Я провожу модульное тестирование бедняги. Если я ленив, то тест-это всего лишь пара допустимых вызовов с потенциально проблемными значениями параметров.
/*
--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....
Проблема модульного тестирования любого вида программирования, связанного с данными, заключается в том, что для начала необходимо иметь надежный набор тестовых данных. Многое также зависит от сложности сохраненного прока и того, что он делает. Было бы очень трудно автоматизировать модульное тестирование для очень сложной процедуры, которая изменила бы многие таблицы.
Некоторые из других плакатов отметили несколько простых способов автоматизации ручного тестирования, а также некоторые инструменты, которые вы можете использовать с SQL Server. На стороне Oracle гуру PL/SQL Стивен Фойерштейн работал над бесплатным инструментом модульного тестирования для хранимых процедур PL/SQL под названием utPLSQL.
Тем не менее, он отказался от этой попытки, а затем пошел на коммерческий рынок с тестером кода Quest для PL/SQL. Quest предлагает бесплатную загружаемую пробную версию. Я нахожусь на грани того, чтобы опробовать его; я понимаю, что он хорошо справляется с накладными расходами при настройке тестовой платформы, чтобы вы могли сосредоточиться только на самих тестах, и он сохраняет тесты, чтобы вы могли повторно использовать их в регрессионном тестировании, что является одним из больших преимуществ test-driven-development. Кроме того, предполагается, что он хорош не только для проверки выходной переменной, но и для проверки изменений данных, но я все равно должен сам посмотреть поближе. Я подумал, что эта информация может быть полезна для пользователей Oracle.
Одним из вариантов пересчета кода (я признаю, что это уродливый Хак) было бы сгенерировать его через CPP (препроцессор C) M4 (никогда не пробовал) или что-то подобное. У меня есть проект, который делает именно это, и он в основном работоспособен.
Единственный случай, который я думаю, что это может быть допустимо, - это 1) как альтернатива KLOC+ хранимым процедурам и 2) и это мои случаи, когда смысл проекта заключается в том, чтобы увидеть, как далеко (в безумие) вы можете продвинуть технологию.