Introducing SqlTest
A library to enable Sql and Ssis Unit Testing
My goal was to create something that was easy to use, easy to debug and easy to get started. SqlTest is meant to be used in a C# test project in conjunction with a test harness like nunit or mstest. I’m well aware that most SQL / SSIS developers do not live in .net and some are downright afraid of it. In fact, another popular testing framework, tSQLt , is an excellent option for many projects because you can write tests in native SQL, plus you get all the goodies that come with it, such as fakes. However, there were several reasons why I chose to go my own way:
- tSQLt wraps everything in transactions, so all
test behavior is rolled back at the end of each test. This is great for database tests, but no good
for SSIS tests which are going to initiate their own connections and
transactions.
- Nunit has parameterized tests (or TestCase
). This allows you to create a dozen related
tests very quickly, each with different input to the same test. This is tremendously efficient.
- By going the route of SqlTest and Nunit, this
allows a consistent approach to testing for both the database and SSIS.
- Database tests in mstest are an elaborate hunk
of junk.
- They don’t allow you to wrap a test in a
transaction and roll it back, if you use Setup / tear down
- They have several layers of set up and tear down
methods, one which executes for a given test and another which executes for
all.
- There are 7 different windows to find a test,
setup, teardown, initialize, cleanup, asserts and assert properties. Never mind that every test is in its own
window. Needless to say, it is a
nightmare to debug.
- Diffs cause eye-pain in source control
- But they do give you syntax highlighting, which is nice. I’m telling you all this so you know what horrors I’m sparing you from
- They don’t allow you to wrap a test in a
transaction and roll it back, if you use Setup / tear down
The initial release of SqlTest contains the essentials for testing. Methods for:
- executing any adhoc sql statement for setting up
and running a test
- fetching a scalar result from the database
- executing a package in the catalog
- faking a table and dropping the fake
- faking a view and dropping the fake
There are certainly some other features which will be useful, like executing a package from the file system for those poor souls who’ve yet to migrate to the project model. And there are some things which are less useful, like asserts on a dataset, which tends to be very fragile. I will plan to add more in time – as well, I hope for some suggested features from you.
There’s documentation about how it works here: https://github.com/MarkWojciechowicz/SqlTest/wiki
I encourage you to try it out today, this is an excellent compliment for any SSDT database project. Just create a new c# project in the same solution.You can install SqlTest from nuget by opening the Package Manager Console in visual studio and typing Install-Package SqlTest.
A sample comes with the install, but below is a quick example to see how to use it.
Enjoy!
using System ; using NUnit.Framework ; using SqlTest ; using System.Transactions ; namespace mynamespace.Tests { public class UnitUnderTest { TransactionScope scope; SqlTest.SqlTestTarget dataWarehouseDb; [SetUp] public void Setup () { scope = new TransactionScope(); dataWarehouseDb = new SqlTest.SqlTestTarget( "DataWarehouse" ); } [TearDown] public void Teardown () { if (Transaction.Current.TransactionInformation.Status == TransactionStatus.Active) { scope.Dispose(); } } [Test] public void UnitUnderTest_Action_ExpectedOutcome () { //Arrange dataWarehouseDb.ExecuteAdhoc( $ "Truncate table / Insert test data;" ); //Act dataWarehouseDb.ExecuteAdhoc( $ "Exec proc;" ); //Assert var actual = dataWarehouseDb.GetActual( "Select scalar value to test outcome" ); Assert.That(actual, Is.EqualTo( "Expected Value" )); //The teardown method runs after every test and will rollback all actions in one transaction } [TestCase("SomeValue", 1)] [TestCase("SomeOtherValue", 2)] public void UnitUnderTest_Action_ExpectedOutcome ( string param, object expected) { //Arrange dataWarehouseDb.ExecuteAdhoc( $ "Truncate table / Insert test data;" ); //Act dataWarehouseDb.ExecuteAdhoc( $ "Exec proc @param = {param};" ); //Assert var actual = dataWarehouseDb.GetActual( "Select to check result" ); Assert.That(actual, Is.EqualTo(expected)); } } }