unit testing for sql prepared for sugsa codelabs alain king paul johnson
TRANSCRIPT
UNIT TESTINGFOR SQL
Prepared for SUGSA CodeLabs
Alain King
Paul Johnson
Unit Testing for SQL 2
Why?
Simply put, we want quality!
Unit Testing for SQL 3
How
• KRS is committed to Behaviour Driven Development (BDD), i.e. using stories and scenarios to drive development.
• With most of the newer technologies, these scenarios turn into unit tests, either in MSTest, Nunit, Junit, and others
Unit Testing for SQL 4
…But…
• We also have big, complex legacy systems that have served us well for many years…
• …and these are mostly written using MS SQL Server stored procedures…
• …and yet we still demand quality, robust, elegant solutions…
Unit Testing for SQL 5
…And…
• Clients have invested in high-end servers to host the database so we want to harness that power for large processing tasks (like month end processes)…
• … and there are a number of functions that happen through SQL code in the BI environment (like the ETL process) …
Unit Testing for SQL 6
Now what?
• How do you safely add new database functionality?
• How do you safely refactor existing stored procedures/functions?
…without breaking the existing system…
Unit Testing for SQL 7
Introducing tSQLt
• tSQLt is a database unit testing framework for Microsoft SQL Server.
• tSQLt is compatible with SQL Server 2005 (service pack 2 required) and above on all editions.
• tSQLt allows you to implement unit tests in T-SQL.
• This is important as you do not have to switch between various tools to create your code and your unit tests.
Unit Testing for SQL 8
tSQLt Features
• Tests are automatically run within transactions – this keeps tests independent and reduces any cleanup work you need
• Tests can be grouped together within a schema – allowing you to organize your tests and use common setup methods
• Output can be generated in plain text or XML – making it easier to integrate with a continuous integration tool
• Provides the ability to fake tables and views, and to create stored procedure spies – allowing you to isolate the code which you are testing
Unit Testing for SQL 9
Examples
Lets look at some sample code• Write a test for a new stored proc
Unit Testing for SQL 10
Examplesbegin tran
select * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Lee Duffy
declare @BusinessEntityID int, @MiddleName varchar(50) select @BusinessEntityID = 2, @MiddleName = 'Leigh'
update Person.Person set MiddleName = @MiddleName where BusinessEntityID = @BusinessEntityID select * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Leigh Duffy
rollback transaction
select * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Lee Duffy
Unit Testing for SQL 11
Examplesselect * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Lee Duffy
begin tran
declare @BusinessEntityID int, @MiddleName varchar(50) select @BusinessEntityID = 2, @MiddleName = 'Leigh'
exec Person.uspUpdatePersonInfo @BusinessEntityID = @BusinessEntityID, @MiddleName = @MiddleName select * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Leigh Duffy
rollback transaction
-- check that data is back to originalselect * from Person.Person where BusinessEntityID = 2
BusinessEntityID PersonType NameStyle Title FirstName MiddleName LastName 2 EM 0 NULL Terri Lee Duffy
Unit Testing for SQL 12
Examplesexec tSQLt.NewTestClass 'TestPerson'godrop proc TestPerson.[test Person middle name updated correctly]gocreate proc TestPerson.[test Person middle name updated correctly]asbegin declare @BusinessEntityID int, @MiddleName varchar(50) select @BusinessEntityID = 2, @MiddleName = 'Leigh'
-- When I update the MiddletName declare @retval int exec @retval = Person.uspUpdatePersonInfo @BusinessEntityID = @BusinessEntityID, @MiddleName = @MiddleName declare @ActualMiddleName varchar(50) select @ActualMiddleName = middlename from Person.Person where BusinessEntityID = @BusinessEntityID exec tSQLt.AssertEqualsString @MiddleName, 'broken' end
exec tSQLt.RunAll
Unit Testing for SQL 13
Examplesexec tSQLt.RunAll
[TestPerson].[test Person middle name updated correctly] failed: Expected: <Leigh> but was: <broken> +----------------------+|Test Execution Summary|+----------------------+ |No|Test Case Name |Result |+--+--------------------------------------------------------+-------+|1 |[TestPerson].[test Person middle name updated correctly]|Failure|-----------------------------------------------------------------------------Msg 50000, Level 16, State 10, Line 1Test Case Summary: 1 test case(s) executed, 0 succeeded, 1 failed, 0 errored.-----------------------------------------------------------------------------
Unit Testing for SQL 14
Examples
• Write a test for a new stored proc
• So what did I do wrong?
• Test First
Unit Testing for SQL 15
Examples
Scenario
Given Person with ID 2 and Middle Name of Lee
When I update the Middle Name to be Leigh
Then the Middle Name for Person with ID 2 should be Leigh
Unit Testing for SQL 16
Examples
Write a test for a new stored proc
• Lets have a look at some code
Unit Testing for SQL 17
Examples
• Write a test to compare 2 result sets
• Upgrading legacy systems to be 2008 / 2012 compliant
• Replacing complex queries with CTE’s to be easier to maintain
• In both cases, you are making changes to code and the expect result should be same
Unit Testing for SQL 18
Examples
• Write a test to compare 2 result sets
• We can check this by writing a test to output the results from the original code into one table and the results of the modified code into another table and comparing them.
• But we don’t just want to compare for one set of inputs
Unit Testing for SQL 19
Examples
• Write a test to compare 2 result sets
• JUnit and NUnit have TestCase functionality that allows one to run the same test with just supplying inputs and expected results as parameters.
Unit Testing for SQL 20
ExamplesEXEC tSQLt.NewTestClass 'test_BillingReports';GO
/* ----------------------------------------------------------------------------- */create procedure test_BillingReports.[test periodbilling (@periodno 201101).]asbegin exec test_BillingReports.periodbilling_sub 201101end;
/* ----------------------------------------------------------------------------- */create procedure test_BillingReports.[test periodbilling (@periodno 201201).]asbegin exec test_BillingReports.periodbilling_sub 201201 end;
/* ----------------------------------------------------------------------------- */create procedure test_BillingReports.[test periodbilling (@periodno 201202).]asbegin exec test_BillingReports.periodbilling_sub 201202 end;
Unit Testing for SQL 21
Examplescreate procedure test_BillingReports.periodbilling_sub(@periodno int)asbegin if object_id('actual') is not null drop table actual; if object_id('expected') is not null drop table expected;
------Assertion create table actual ( columns )
create table expected ( columns )
--OtherDatabase has the original version of the sproc insert expected ( columns ) exec OtherDatabase..periodbilling @periodno = @periodno
insert actual ( columns ) exec periodbilling @periodno = @periodno
exec tSQLt.assertEqualsTable 'expected', 'actual'; end;go
EXEC tSQLt.run 'test_BillingReports';
Unit Testing for SQL 22
Examples
• Write a test to compare 2 result sets
• Lets take a look at some more code
Unit Testing for SQL 23
Examples
• Write a test for an existing stored procedure
• This can be used for DDT – Defect Driven Testing or for enhancing existing functionality.
• Lets look at a test for an existing procedure in AdventureWorks called uspGetEmployeeManagers
Unit Testing for SQL 24
Exercises
• Download from tsqlt.org
• exec tSQLt.NewTestClass 'TestPerson'• Go
• create proc TestPerson.[test UpdatePersonMiddleName]• as
• exec tSQLt.AssertEqualsString @MiddleName, @ActualMiddleName
• end
• exec tSQLt.RunAll
Unit Testing for SQL 25
Exercises
• Write a test for a new stored procedure to check if an email address already exists in the database
Unit Testing for SQL 26
Exercises
• Write a test for a new stored procedure to update available leave hours for an employee
Unit Testing for SQL 27
Exercises
• We want to make changes to an existing stored procedure, write a test for uspGetBillOfMaterials to ensure that any changes you make do not have a negative impact on existing functionality.
28
Questions?
www.krs.co.za
( (021) 681 2900Alain King: [email protected] Johnson: [email protected]
Unit Testing for SQL