Procedure And Test Development In Sql Server

Procedure and Test Development in SQL Server

The Example

The system under test (SUT) will be added to a number of database applications to create daily batches of transaction summaries for billing our clients. The applications already have common code for processing batches and manipulating the state of transactions. This example will make use of these features without explanation. Hopefully the usage will be obvious.

Each client can be billed for a number of services, and each service is identified as a partner, and service partners belong to the BillingEvent partner group. The PartnerMap table needs to be populated for the inner working of the applications.

The Batcher is configured for the batches being created.

Two state machines are defined to identify billable transactions (Dispatcher), and to track the billing transactions created (EventTracker). These machines define the valid state transitions available to transactions.

Objective

We will be creating a script to create the stored procedure, and a number of scripts that show the data created by the procedure. In this blog we focus on the Partner configuration.

Test scenario

For testing, we will use the following variables:

DECLARE
    @service            VARCHAR(50) = 'TEST',
    @companyCode        VARCHAR(50) = 'TST',
    @coPartnerCode      VARCHAR(50) = 'Ecs',
    @partnerCategory    VARCHAR(50) = 'StoreCode'

We will be creating the following:

  • Partner
    • BillingEvent:TEST
  • PartnerGroup
    • BillingEvent
  • PartnerMap
    • BillingEvent:Company = TST
    • BillingEvent:CoPartner = Ecs
    • BillingEvent:PartnerCategory = StoreCode
    • BillingEvent:Service = TEST

Create a working script

We test this with these queries:

DECLARE @partnerID INT = dbo.MapPartner('BillingEvent:TEST')

SELECT  *
FROM    dbo.Partner
WHERE   partnerID = @partnerID

SELECT  *
FROM    dbo.PartnerGroup
WHERE   partnerID = @partnerID

SELECT  *
FROM    dbo.PartnerMap
WHERE   partnerID = @partnerID
    AND context LIKE 'BillingEvent:%'
ORDER BY context

At this stage we will be testing by looking at the result. We will be running our test repeatedly, so we put it in a transaction that we can roll back. Our script now looks like this:

BEGIN TRANSACTION

DECLARE
    @service            VARCHAR(50) = 'TEST',
    @companyCode        VARCHAR(50) = 'TST',
    @coPartnerCode      VARCHAR(50) = 'Ecs',
    @partnerCategory    VARCHAR(50) = 'StoreCode'

-- something will go here

DECLARE @partnerID INT = dbo.MapPartner('BillingEvent:TEST')

SELECT  *
FROM    dbo.Partner
WHERE   partnerID = @partnerID

SELECT  *
FROM    dbo.PartnerGroup
WHERE   partnerID = @partnerID

SELECT  *
FROM    dbo.PartnerMap
WHERE   partnerID = @partnerID
    AND context LIKE '%'
ORDER BY context

ROLLBACK

Implementing the functionality

We get each part to work in turn.

Partner

We create a partner making use of the parameters:

DECLARE @partner VARCHAR(50) = 'BillingEvent:' + @service
DECLARE @partnerID INT
EXEC    AddPartner @partner = @partner, @partnerID = @partnerID OUTPUT

--DECLARE   @partnerID INT = dbo.MapPartner('BillingEvent:TEST')
SET     @partnerID = dbo.MapPartner('BillingEvent:TEST')

Note that I have temporarily replaced the declaration of @partnerID with a SET command. My plan is to refactor the configuration code into a procedure, but for now it is convenient to do our coding inline.

When we execute the script, we get the following output:

partnerID partner enabled dateEnabledChanged
12 BillingEvent:TEST 1 2018-09-19 13:28:40.657
group partnerID
   
context aliasID alias partnerID
       

Later we will automate the test by comparing the result with our expectation. Now will be a good time to tidy up the queries to show only the values we want to compare:

SELECT  partner
FROM    dbo.Partner
WHERE   partnerID = @partnerID

SELECT  [group] partnerGroup
FROM    dbo.PartnerGroup
WHERE   partnerID = @partnerID

SELECT  context, aliasID, alias
FROM    dbo.PartnerMap
WHERE   partnerID = @partnerID
    AND	context LIKE 'BillingEvent:%'
ORDER BY context

yielding

partner
BillingEvent:TEST
partnerGroup
 
context aliasID alias
     

Partner Group

We add

EXEC    AddPartnerGroup @partnerID = @partnerID, @group = 'BillingEvent'

yielding

partner
BillingEvent:TEST
partnerGroup
BillingEvent
context aliasID alias
     

Partner Maps

To see what happens, we add

EXEC    AddPartnerMap @context = 'BillingEvent:Service', @aliasID = @service, @alias = @service, @partnerID = @partnerID

and get

partner
BillingEvent:TEST
partnerGroup
BillingEvent
context aliasID alias
BillingEvent:Service TEST TEST

Looking good! We add the remaining partner maps and get

partner
BillingEvent:TEST
partnerGroup
BillingEvent
context aliasID alias
BillingEvent:Company TST TST
BillingEvent:CoPartner Ecs Ecs
BillingEvent:PartnerCategory StoreCode StoreCode
BillingEvent:Service TEST TEST

The test script

Our script now looks like this:

BEGIN TRANSACTION

DECLARE @service AS VARCHAR (50), @companyCode AS VARCHAR (50), @coPartnerCode AS VARCHAR (50), @partnerCategory AS VARCHAR (50);

SELECT  @service = 'TEST',
        @companyCode = 'TST',
        @coPartnerCode = 'Ecs',
        @partnerCategory = 'StoreCode';

DECLARE @partner VARCHAR(50) = 'BillingEvent:' + @service
DECLARE @partnerID INT
EXEC    AddPartner @partner = @partner, @partnerID = @partnerID OUTPUT
EXEC    AddPartnerGroup @partnerID = @partnerID, @group = 'BillingEvent'
EXEC    AddPartnerMap @context = 'BillingEvent:Service', @aliasID = @service, @alias = @service, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:Company', @aliasID = @companyCode, @alias = @companyCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:CoPartner', @aliasID = @coPartnerCode, @alias = @coPartnerCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:PartnerCategory', @aliasID = @partnerCategory, @alias = @partnerCategory, @partnerID = @partnerID

--DECLARE   @partnerID INT = dbo.MapPartner('BillingEvent:TEST')
SET     @partnerID = dbo.MapPartner('BillingEvent:TEST')

SELECT  partner
FROM    dbo.Partner
WHERE   partnerID = @partnerID

SELECT  [group] partnerGroup
FROM    dbo.PartnerGroup
WHERE   partnerID = @partnerID

SELECT  context, aliasID, alias
FROM    dbo.PartnerMap
WHERE   partnerID = @partnerID
    AND context LIKE 'BillingEvent:%'
ORDER BY context

ROLLBACK

Extracting the procedure

We replace the code

DECLARE @partner VARCHAR(50) = 'BillingEvent:' + @service
DECLARE @partnerID INT
EXEC    AddPartner @partner = @partner, @partnerID = @partnerID OUTPUT
EXEC    AddPartnerGroup @partnerID = @partnerID, @group = 'BillingEvent'
EXEC    AddPartnerMap @context = 'BillingEvent:Service', @aliasID = @service, @alias = @service, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:Company', @aliasID = @companyCode, @alias = @companyCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:CoPartner', @aliasID = @coPartnerCode, @alias = @coPartnerCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:PartnerCategory', @aliasID = @partnerCategory, @alias = @partnerCategory, @partnerID = @partnerID

with

EXECUTE [BillingEvent].[GenericBillingServiceSetup] @service, @companyCode, @coPartnerCode, @partnerCategory;

and uncomment

--DECLARE   @partnerID INT = dbo.MapPartner('BillingEvent:TEST')

to yield

BEGIN TRANSACTION

-- database unit test for BillingEvent.GenericBillingServiceSetup
DECLARE @service AS VARCHAR (50), @companyCode AS VARCHAR (50), @coPartnerCode AS VARCHAR (50), @partnerCategory AS VARCHAR (50);

SELECT  @service = 'TEST',
        @companyCode = 'TST',
        @coPartnerCode = 'Ecs',
        @partnerCategory = 'StoreCode';

EXEC [BillingEvent].[GenericBillingServiceSetup] @service, @companyCode, @coPartnerCode, @partnerCategory;

DECLARE @partnerID INT = dbo.MapPartner('BillingEvent:TEST')
SET     @partnerID = dbo.MapPartner('BillingEvent:TEST')

SELECT  partner
FROM    dbo.Partner
WHERE   partnerID = @partnerID

SELECT  [group] partnerGroup
FROM    dbo.PartnerGroup
WHERE   partnerID = @partnerID

SELECT  context, aliasID, alias
FROM    dbo.PartnerMap
WHERE   partnerID = @partnerID
    AND	context LIKE 'BillingEvent:%'
ORDER BY context

ROLLBACK

The stored procedure is like this

CREATE PROCEDURE [BillingEvent].[GenericBillingServiceSetup]
    @service AS VARCHAR (50),
    @companyCode AS VARCHAR (50),
    @coPartnerCode AS VARCHAR (50),
    @partnerCategory AS VARCHAR (50)
AS
DECLARE @partner VARCHAR(50) = 'BillingEvent:' + @service
DECLARE @partnerID INT
EXEC    AddPartner @partner = @partner, @partnerID = @partnerID OUTPUT
EXEC    AddPartnerGroup @partnerID = @partnerID, @group = 'BillingEvent'
EXEC    AddPartnerMap @context = 'BillingEvent:Service', @aliasID = @service, @alias = @service, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:Company', @aliasID = @companyCode, @alias = @companyCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:CoPartner', @aliasID = @coPartnerCode, @alias = @coPartnerCode, @partnerID = @partnerID
EXEC    AddPartnerMap @context = 'BillingEvent:PartnerCategory', @aliasID = @partnerCategory, @alias = @partnerCategory, @partnerID = @partnerID

Running the script yields

partner
BillingEvent:TEST
partnerGroup
BillingEvent
context aliasID alias
BillingEvent:Company TST TST
BillingEvent:CoPartner Ecs Ecs
BillingEvent:PartnerCategory StoreCode StoreCode
BillingEvent:Service TEST TEST

which hits the nail on the head!