Unit testing is a crucial aspect of software development, and this holds true for SQL Server as well. Many developers often question the value of writing unit tests, believing that it requires a significant effort and may not catch all bugs. However, the benefits of writing unit tests far outweigh the perceived challenges.
One area where unit tests can be particularly useful in SQL Server is when writing queries that involve running totals. While experienced T-SQL developers may find this task relatively straightforward, it is easy to make mistakes. Let’s consider a simple example:
SELECT
CustomerID,
OrderDate,
CustomerRunningTotal =
(
SELECT CustomerRunningTotal = SUM(TotalDue)
FROM Sales.SalesOrderHeader
WHERE
CustomerID = A.CustomerID
AND OrderDate <= A.OrderDate
)
FROM Sales.SalesOrderHeader AS A
ORDER BY
A.CustomerID,
A.OrderDate;
This query produces a large number of results, and it is impractical to manually validate each one. In fact, this query contains a bug that can easily go unnoticed. To address this, we can rewrite the query using window functions, which are available in SQL Server 2012 and later versions.
Now, instead of manually experimenting with the query and checking a few results, we can write a unit test to ensure the accuracy of our code. Let’s walk through the process of creating a simple test:
EXEC tsqlt.NewTestClass @ClassName = N'ztsqltests'
GO
CREATE PROCEDURE [ztsqltests].[test running total query on Sales Order Header]
AS
BEGIN
-- Assemble
DECLARE @expected INT, @actual INT
-- Act
-- Assert
EXEC tsqlt.AssertEquals @Expected = @expected,
@Actual = @actual,
@Message = N'Incorrect result'
END
GO
In the above example, we have created a new test class and a test procedure. The test follows a basic structure:
- Assemble an environment
- Act on some data
- Assert a truth
In the “Assemble” section, we set up the necessary data for the test. This can be done using tools like tsql.faketable to create a copy of the table we are testing. We can then insert test data into this table.
The “Act” section is where we execute our code or stored procedure that we want to test. In this case, we can call our stored procedure that contains the running total query.
Finally, in the “Assert” section, we compare the expected results with the actual results using assertions provided by the testing framework. This ensures that our code is producing the desired outcome.
By writing unit tests, we can catch bugs and ensure the correctness of our code. Unit tests are repeatable and can be automated, allowing for easy regression testing. They also serve as documentation for our thought process as developers and can be shared with others for review.
While writing unit tests may initially seem challenging, with practice and the use of templates or snippets, it becomes a streamlined process. Starting with testing code that is being refactored or modified is a good approach. Over time, we can build up a comprehensive set of tests to cover different scenarios.
Unit testing is an essential part of the software development lifecycle, and SQL Server is no exception. It helps us write better code, catch bugs early, and ensure the reliability of our database applications.