How to unit test SQL Server 2008 database using Visual Studio 2010 – Part 3
This is the last article of the series “How to unit test SQL Server 2008 database using Visual Studio 2010”.
This is the last article of the series “How to unit test SQL Server 2008 database using Visual Studio 2010”. The previous articles are
Unit test SQL Server 2008 - Part 1 – Focused on setting up environment, creating database project and executing simple unit tests
Unit test SQL Server 2008 - Part 2 – Focused on internals of database unit testing i.e. assemblies
I’ll divide this article into two logical parts
-
Tools that are useful for creation of test data as well as ensuring that database schemas are synchronized
-
Data generation
-
Schema comparison
-
Data comparison
-
-
More about unit testing a database which include
-
Data driven unit tests
-
Pre, test and post scripts
-
Test conditions
-
Transactions
-
Data Generation Plan:
Test data is created for verifying the behaviour of database objects in a database project. Generating data that is appropriate for the schema of the database but unrelated to the production data helps protect the privacy or security of the production data. Data generation plan can be added to the database project as displayed below
When data generation plan is created for a database project, the plan is based on the schema of the project. If after creation of the plan schema changes, Visual Studio prompts to update the plan.
There are two types of templates available for creating data generation plan
-
Data Generation Plan: This template creates an empty data generation plan
-
Data Transform Plan: This template creates an data generation plan reusing data from another data source
Data generation plan Template:
Select the template i.e. Data Generation Plan and specify name and Add as displayed below
After you click Add you can customize various aspects of data generation plan in the Data generation plan designer as displayed below
Here three tables are displayed as I had three tables Event, EventPass and Student in my database project. The Related Table shows a dropdown only for EventPass table because EventPass has foreign key relation to Event and Student table. Please refer to first article for details.
You can choose Table and Rows to Insert in each table for all the tables provided for table that has related tables e.g. EventPass Related Table is set to None. If Related Table is chosen then you can specify the Ratio to Related Table and depending upon this ratio the Rows to Insert column will be populated.
You can preview as well as change the generator as displayed below
Once you are done configuring this plan go to Data –> Data Generator –> Generate Data as displayed below
Specify a new or an existing connection in the Connect to Database dialog that will appear next. Please note that you need to have the same schema in the database that you have specified for this else Data generation will fail. In this example my database is SampleDBTestData and it contains tables only. Once data generation is completed you can see the generate data in the database that you specified.
Data transform plan Template:
Select the Data transform plan template, specify name and Add as displayed below
I have highlighted the sections that can be configured in this dialog. Rest of the steps are same as discussed above i.e. once you are done configuring this plan go to Data –> Data Genarator –> Generate Data.
Schema Comparison:
Schema comparison is a powerful tool that is used to compare database schemas as displayed below
The result when source and target schema are same is displayed below
The result when source and target schema were somewhat different is displayed below
Data Comparison:
The data from two databases can be compared as display below
This concludes the first logical part.
Data driven unit tests
In order to write a data driven unit test two important characteristics that we use are
-
DataSourceAttribute: This attribute provides data source specific information for data driven testing and is available in assembly Microsoft.VisualStudio.QualityTools.UnitTestFramework
-
TestContext: This class is used to store information that is provided to unit tests and is available in assembly Microsoft.VisualStudio.QualityTools.UnitTestFramework
As displayed in code snippet I have marked the dbo_AddStudentTest with DataSource and TestMethod attributes and specified the provider type, connection string, table name and data access method as displayed below
[DataSource("System.Data.SqlClient", @"Data Source=SQLSERVER2008;Initial Catalog=TestData;Persist Security Info=True;User ID=sa;Password=*****;Pooling=False", "EmployeeData", DataAccessMethod.Sequential), TestMethod()]
object parameterValue = TestContext.DataRow.ItemArray.GetValue(0);
-- unit test for dbo.uspAddStudent DECLARE @RC AS INT SELECT @RC = 0 EXECUTE @RC = [dbo].[uspAddStudent] @StudentName; SELECT * FROM [dbo].[Student];
[DataSource("System.Data.SqlClient", @"Data Source=SQLSERVER2008;Initial Catalog=TestData;Persist Security Info=True;User ID=sa;Password=*******;Pooling=False", "EmployeeData", DataAccessMethod.Sequential), TestMethod()] public void dbo_uspAddStudentTest() { DatabaseTestActions testActions = this.dbo_uspAddStudentTestData; // Execute the pre-test script System.Diagnostics.Trace.WriteLineIf((testActions.PretestAction != null), "Executing pre-test script..."); ExecutionResult[] pretestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PretestAction); //Specifying the value of parameter from TestContext object parameterValue = TestContext.DataRow.ItemArray.GetValue(0); DbParameter parameter= CreateDBParameter("StudentName", ParameterDirection.Input, DbType.String, 100, parameterValue); // Execute the test script System.Diagnostics.Trace.WriteLineIf((testActions.TestAction != null), "Executing test script..."); ExecutionResult[] testResults = TestService.Execute(this.ExecutionContext, this.PrivilegedContext, testActions.TestAction, parameter); // Execute the post-test script System.Diagnostics.Trace.WriteLineIf((testActions.PosttestAction != null), "Executing post-test script..."); ExecutionResult[] posttestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PosttestAction); private DbParameter CreateDBParameter(string name, ParameterDirection direction, DbType dbType, int size, object value) DbParameter parameter = base.ExecutionContext.Provider.CreateParameter(); parameter.Direction = direction; parameter.DbType = DbType.String; parameter.ParameterName = name; parameter.Size = size; parameter.Value = value; return parameter; }
<configSections> <section name="microsoft.visualstudio.testtools" type="Microsoft.VisualStudio.TestTools.UnitTesting.TestConfigurationSection, Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/> </configSections> <connectionStrings> <add name="sqlDataCon" connectionString="Data Source=SQLSERVER2008;Initial Catalog=TestData;Persist Security Info=True;User ID=sa;Password=*********;Pooling=False" providerName ="System.Data.SqlClient"/> </connectionStrings> <microsoft.visualstudio.testtools> <dataSources> <add name ="sqlDataSource" connectionString="sqlDataCon" dataTableName="EmployeeData" dataAccessMethod="Sequential"/> </dataSources> </microsoft.visualstudio.testtools><
[DataSource("sqlDataSource")] [TestMethod] public void dbo_uspAddStudentTest()
Common, Pre, Test and Post scripts:
You can specify Common scripts(common to all tests), Pre and Post scripts to a test in test editor. By default these are optional. The order of execution of these scripts is
Test Conditions:
I have discussed the test conditions in previous articles. In this article I’ll show two properties i.e. Configuration and Enabled. Enabled exist across all test conditions. You can enable/disable a test condition using this. Configuration property exists for Expected Schema and Data Checksum test conditions. For these conditions you need to configure the result you are expecting by clicking the button as highlighted below
Transactions:
There are three ways through which we can specify transactions as listed below- Inside Transact-SQL
- ADO.NET Transactions: Transactions can be implemented by adding code snippet to the test method as displayed below
base.ExecutionContext.Connection.BeginTransaction()
- System.Transactions: Transactions can be implemented by adding code snippet to the test method as displayed below
using (TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required)) { base.PrivilegedContext.Connection.EnlistTransaction(Transaction.Current); base.ExecutionContext.Connection.EnlistTransaction(Transaction.Current); //Rest of the test method code }