Skip to content

Commit 6ac2b09

Browse files
GitHub pages - isolating dependencies (#172)
* created isolating dependencies folder * Add Isolating dependencies landing page * Add Apply constraint page * Add Apply trigger page * Created pages for isolating dependencies * Updated index.md * Add Fake function page Co-authored-by: olhakonst17 <58791358+olhakonst17@users.noreply.github.com>
1 parent 2254841 commit 6ac2b09

File tree

8 files changed

+583
-0
lines changed

8 files changed

+583
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# ApplyConstraint
2+
## Syntax
3+
4+
``` sql
5+
tSQLt.ApplyConstraint [@TableName = ] 'table name'
6+
, [@ConstraintName = ] 'constraint name'
7+
, [@SchemaName = ] 'schema name'
8+
```
9+
10+
## Arguments
11+
12+
[**@TableName** = ] ‘table name’
13+
14+
The name of the table where the constraint should be applied. Should contain both the schema name and the table name.
15+
16+
[**@ConstraintName** = ] ‘constraint name’
17+
18+
The name of the constraint to be applied. Should not include the schema name or table name.
19+
20+
[**@SchemaName** = ] ‘schema name’ – **Deprecated: do not use, will be removed in future version**
21+
22+
## Return code values
23+
Returns 0
24+
25+
## Error raised
26+
27+
If the specified table or constraint does not exist an error is thrown: ApplyConstraint could not resolve the object names, ‘%s’, ‘%s’.
28+
29+
## Result sets
30+
None
31+
32+
## Overview
33+
We want to be able to test constraints individually. We can use FakeTable to remove all the constraints on a table, and ApplyConstraint to add back in the one which we want to test.
34+
35+
ApplyConstraint in combination with FakeTable allows constraints to be tested in isolation of other constraints on a table.
36+
37+
## Limitations
38+
ApplyConstraint works with the following constraint types:
39+
40+
- CHECK constraints
41+
- FOREIGN KEY constraints
42+
- UNIQUE constraints
43+
- PRIMARY KEY constraints
44+
45+
There are the following limitations:
46+
47+
- Cascade properties of FOREIGN KEY constraints are not preserved.
48+
- SQL Server automatically creates unique indexes for UNIQUE and PRIMARY KEY constraints. Those indexes for “applied” constraints do not preserve asc/desc properties of the original supporting indexes.
49+
50+
Note: Applying a PRIMARY KEY constraint will automatically change the involved columns of the faked table to be “NOT NULL”able.
51+
52+
## Examples
53+
**Example: Using ApplyConstraint to test a Foreign Key Constraint**
54+
55+
In this example, we have a foreign key constraint on the ReferencingTable. We would like to test this constraint and have two test cases.
56+
57+
The first test ensures that the foreign key prevents inappropriate inserts. It does this by faking the two tables involved and then calling ApplyConstraint. Then an attempt is made to insert a record into the ReferencingTable with no record in the ReferencedTable. The exception is caught and the test passes or fails based on this exception.
58+
59+
The second test makes sure that appropriate records can be inserted. Again, the two tables are faked and ApplyConstraint is called. If any exception is thrown when we attempt to insert a record into ReferencingTable, the test will fail (because any uncaught exception will cause a test to fail).
60+
61+
``` sql
62+
EXEC tSQLt.NewTestClass 'ConstraintTests';
63+
GO
64+
65+
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK prevents insert of orphaned rows]
66+
AS
67+
BEGIN
68+
EXEC tSQLt.FakeTable 'dbo.ReferencedTable';
69+
EXEC tSQLt.FakeTable 'dbo.ReferencingTable';
70+
71+
EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK';
72+
73+
DECLARE @ErrorMessage NVARCHAR(MAX); SET @ErrorMessage = '';
74+
75+
BEGIN TRY
76+
INSERT INTO dbo.ReferencingTable
77+
( id, ReferencedTableId )
78+
VALUES ( 1, 11 ) ;
79+
END TRY
80+
BEGIN CATCH
81+
SET @ErrorMessage = ERROR_MESSAGE();
82+
END CATCH
83+
84+
IF @ErrorMessage NOT LIKE '%ReferencingTable_ReferencedTable_FK%'
85+
BEGIN
86+
EXEC tSQLt.Fail 'Expected error message containing ''ReferencingTable_ReferencedTable_FK'' but got: ''',@ErrorMessage,'''!';
87+
END
88+
89+
END
90+
GO
91+
92+
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK allows insert of non-orphaned rows]
93+
AS
94+
BEGIN
95+
EXEC tSQLt.FakeTable 'dbo.ReferencedTable';
96+
EXEC tSQLt.FakeTable 'dbo.ReferencingTable';
97+
98+
EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK';
99+
100+
INSERT INTO dbo.ReferencedTable
101+
( id )
102+
VALUES ( 11 ) ;
103+
INSERT INTO dbo.ReferencingTable
104+
( id, ReferencedTableId )
105+
VALUES ( 1, 11 ) ;
106+
END
107+
GO
108+
```
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# ApplyTrigger
2+
## Syntax
3+
4+
``` sql
5+
tSQLt.ApplyTrigger [@TableName = ] 'table name'
6+
, [@TriggerName = ] 'trigger name'
7+
```
8+
9+
## Arguments
10+
11+
[**@TableName** = ] ‘table name’
12+
The name of the table where the constraint should be applied. Should contain both the schema name and the table name.
13+
14+
[**@TriggerName** = ] ‘trigger name’
15+
16+
The name of the trigger to be applied. Should not include the schema name or table name.
17+
18+
## Return code values
19+
Returns 0
20+
21+
## Error raised
22+
If the specified table or trigger does not exist an error is thrown.
23+
24+
## Result sets
25+
None
26+
27+
## Overview
28+
We want to be able to test triggers individually. We can use FakeTable to remove all the constraints and triggers from a table, and ApplyTrigger to add back in the one which we want to test.
29+
30+
ApplyTrigger in combination with FakeTable allows triggers to be tested in isolation of constraints and other triggers on a table.
31+
32+
## Examples
33+
**Example: Using ApplyTrigger to test a trigger**
34+
35+
In this example, the test isolates the AuditInserts trigger from other constraints and triggers on the Registry.Student table. This allows us to test that the trigger inserts a record into the Logs.Audit table when a new student is inserted into the Student table.
36+
37+
``` sql
38+
EXEC tSQLt.NewTestClass 'AuditTests';
39+
GO
40+
41+
CREATE PROCEDURE AuditTests.[test inserting record into Student table creates Audit record]
42+
AS
43+
BEGIN
44+
EXEC tSQLt.FakeTable 'Registry.Student';
45+
EXEC tSQLt.FakeTable @TableName = 'Logs.Audit';
46+
EXEC tSQLt.ApplyTrigger 'Registry.Student', 'AuditInserts';
47+
48+
INSERT INTO Registry.Student (StudentId) VALUES (1);
49+
50+
SELECT LogMessage
51+
INTO #Actual
52+
FROM Logs.Audit;
53+
54+
SELECT TOP(0) *
55+
INTO #Expected
56+
FROM #Actual;
57+
58+
INSERT INTO #Expected
59+
VALUES('Student record created, id = 1');
60+
61+
EXEC tSQLt.AssertEqualsTable '#Expected','#Actual';
62+
END;
63+
GO
64+
```
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# FakeFunction
2+
3+
## Syntax
4+
5+
``` sql
6+
tSQLt.FakeFunction [@FunctionName = ] 'function name'
7+
, [@FakeFunctionName = ] 'fake function name'
8+
```
9+
10+
## Arguments
11+
12+
[**@FunctionName** = ] ‘function name’
13+
14+
The name of an existing function. @FunctionName is NVARCHAR(MAX) with no default. @FunctionName should include the schema name of the function. For example: MySchema.MyFunction
15+
16+
[**@FakeFunctionName** = ] ‘fake function name’
17+
18+
The name of an existing function that will replace the function defined by @FunctionName during the test. @FakeFunctionName is NVARCHAR(MAX) with no default.
19+
20+
## Return code values
21+
Returns 0
22+
23+
## Error raised
24+
25+
If the function itself or the fake function does not exist, the follow error is raised: ‘function name’ does not exist
26+
27+
If the function and the fake function are not compatible function types (i.e. they must both be scalar functions or both be table valued functions), the following error is raised: Both parameters must contain the name of either scalar or table valued functions!
28+
29+
If the parameters of the function and fake function are not the same, the following error is raised: Parameters of both functions must match! (This includes the return type for scalar functions.)
30+
31+
## Result sets
32+
None
33+
34+
## Overview
35+
Code that calls a function can be difficult to test if that function performs significant logic. We want to isolate the code we are testing from the logic buried in the functions that it calls. To create independent tests, we can replace a called function with a fake function. The fake function will perform much simpler logic that supports the purpose of our test. Often, the fake function will simply return a hard-coded value.
36+
37+
Alternatively, the fake function may ‘validate’ the parameters it receives by returning one value if the parameters match expectations, and another value if the parameters do not match expectations. That way the code that calls the function will have a different result and thus the parameter passed to the function can be tested.
38+
39+
## Warnings
40+
Remember that if you are faking a function, you are not testing that function. Your test is trying to test something else: typically, the logic of a view, stored procedure or another function that interacts with the function you are faking.
41+
42+
## Examples
43+
**Example: Using FakeFunction to avoid executing the logic of a complex function**
44+
45+
In this example, we want to test a sales report view, SalesReport. The sales report view will return the EmployeeId, RevenueFromSales (the total amount of new revenue the employee generated) and their Commission. (The commision has to be calculated with a complex algorithm using RevenueFromSales and values read from the EmployeeCompensation table. This computation is done by the ComputeCommision scalar function.)
46+
47+
Since we are testing the SalesReport view, we will fake the ComputeCommission function.
48+
49+
``` sql
50+
EXEC tSQLt.NewTestClass 'SalesAppTests';
51+
GO
52+
53+
CREATE FUNCTION SalesAppTests.Fake_ComputeCommission (
54+
@EmployeeId INT,
55+
@RevenueFromSales DECIMAL(10,4)
56+
)
57+
RETURNS DECIMAL(10,4)
58+
AS
59+
BEGIN
60+
RETURN 1234.5678;
61+
END;
62+
GO
63+
64+
CREATE PROCEDURE SalesAppTests.[test SalesReport returns revenue and commission]
65+
AS
66+
BEGIN
67+
-------Assemble
68+
EXEC tSQLt.FakeFunction 'SalesApp.ComputeCommission', 'SalesAppTests.Fake_ComputeCommission';
69+
EXEC tSQLt.FakeTable 'SalesApp.Employee';
70+
EXEC tSQLT.FakeTable 'SalesApp.Sales';
71+
72+
INSERT INTO SalesApp.Employee (EmployeeId) VALUES (1);
73+
INSERT INTO SalesApp.Sales (EmployeeId, SaleAmount) VALUES (1, 10.1);
74+
INSERT INTO SalesApp.Sales (EmployeeId, SaleAmount) VALUES (1, 20.2);
75+
76+
-------Act
77+
SELECT EmployeeId, RevenueFromSales, Commission
78+
INTO SalesAppTests.Actual
79+
FROM SalesApp.SalesReport;
80+
81+
-------Assert
82+
SELECT TOP(0) *
83+
INTO SalesAppTests.Expected
84+
FROM SalesAppTests.Actual;
85+
86+
INSERT INTO SalesAppTests.Expected (EmployeeId, RevenueFromSales, Commission)
87+
VALUES (1, 30.3, 1234.5678);
88+
89+
EXEC tSQLt.AssertEqualsTable 'SalesAppTests.Expected', 'SalesAppTests.Actual';
90+
END;
91+
GO
92+
```
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# FakeTable
2+
3+
## Syntax
4+
5+
```sql
6+
tSQLt.FakeTable [@TableName = ] 'table name'
7+
, [[@SchemaName = ] 'schema name']
8+
, [[@Identity = ] 'preserve identity']
9+
, [[@ComputedColumns = ] 'preserve computed columns']
10+
, [[@Defaults = ] 'preserve default constraints']
11+
```
12+
13+
## Arguments
14+
15+
[**@TableName =** ] ‘table name’
16+
17+
The name of the table for which to create a fake table. Should contain both the schema name and the table name.
18+
19+
[**@SchemaName =** ] ‘schema name’ – **Deprecated: do not use, will be removed in future version**
20+
21+
[**@Identity =** ] ‘preserve identity’ – Indicates if the identity properties of an identity column should be preserved. If @Identity = 1, the identity properties will be preserved, otherwise the faked table will have the identity properties removed. The default is @Identity = 0.
22+
23+
[**@ComputedColumns =** ] ‘preserve computed columns’ – Indicates if the computations on computed columns should be preserved. If @ComputedColumns = 1, the computations will be preserved, otherwise the faked table will have computations removed from computed columns. The default is @ComputedColumns = 0.
24+
25+
[**@Defaults =** ] ‘preserve default constraints’ – Indicates if the default constraints on columns should be preserved. If @Defaults = 1, default constraints will be preserved for all columns on the faked table, otherwise the faked table will not contain default constraints. The default is @Defaults = 0.
26+
27+
## Return Code Values
28+
29+
Returns 0
30+
31+
## Error Raised
32+
33+
If the specified table does not exist an error is thrown: FakeTable could not resolve the object name, ‘%s’.
34+
35+
## Result Sets
36+
37+
None
38+
39+
## Overview
40+
41+
We want to keep our test cases focused and do not want to insert data which is irrelevant to the current test. However, table and column constraints can make this difficult.
42+
43+
FakeTable allows tests to be written in isolation of the constraints on a table. FakeTable creates an empty version of the table without the constraints in place of the specified table. Therefore any statements which access the table during the execution of the test case are actually working against the fake table with no constraints. When the test case completes, the original table is put back in place because of the rollback which tSQLt performs at the end of each test case.
44+
45+
FakeTable can be called on Tables, Views and Synonyms (A Synonym has to point to a table or view in the current database.)
46+
47+
## Limitations
48+
49+
FakeTable cannot be used with temporary tables (tables whose names begin with # or ##).
50+
51+
## Warnings
52+
53+
Remember if you are faking a table, you are not testing the constraints on the table. You will want test cases that also exercise the constraints on the table. See Also: ApplyConstraint.
54+
55+
## Examples
56+
57+
**Example: Faking a table to create test data to test a view**
58+
59+
In this example, we have a FinancialApp.ConvertCurrencyUsingLookup view which accesses the FinancialApp.CurrencyConversion table. In order to test the view, we want to freely insert data into the table. FakeTable is used to remove the constraints from the CurrencyConversion table so that the data record can be inserted.
60+
61+
```sql
62+
CREATE PROCEDURE testFinancialApp.[test that ConvertCurrencyUsingLookup converts using conversion rate in CurrencyConversion table]
63+
AS
64+
BEGIN
65+
DECLARE @expected MONEY; SET @expected = 3.2;
66+
DECLARE @actual MONEY;
67+
DECLARE @amount MONEY; SET @amount = 2.00;
68+
DECLARE @sourceCurrency CHAR(3); SET @sourceCurrency = 'EUR';
69+
DECLARE @destCurrency CHAR(3); SET @destCurrency = 'USD';
70+
71+
------Fake Table
72+
EXEC tSQLt.FakeTable 'FinancialApp.CurrencyConversion';
73+
74+
INSERT INTO FinancialApp.CurrencyConversion (id, SourceCurrency, DestCurrency, ConversionRate)
75+
VALUES (1, @sourceCurrency, @destCurrency, 1.6);
76+
------Execution
77+
SELECT @actual = amount FROM FinancialApp.ConvertCurrencyUsingLookup(@sourceCurrency, @destCurrency, @amount);
78+
79+
------Assertion
80+
EXEC tSQLt.assertEquals @expected, @actual;
81+
END;
82+
GO
83+
```
84+
85+
**Example: Calling FakeTable with @Identity = 1**
86+
87+
We need to specify the @Identity parameter explicitly because the second parameter (@SchemaName) has been deprecated. This same pattern applies to @ComputedColumns and @Defaults.
88+
89+
```sql
90+
EXEC tSQLt.FakeTable 'FinancialApp.CurrencyConversion', @Identity = 1;
91+
```
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Isolating dependencies
2+
3+
This group of objects enables you to isolate the are of code which you are testing. When you are unit testing a complex system, it is desirable to isolate certain parts to test. For example, if you have a complicated set of tables which are related through foreign keys, it can be very difficult to insert test data into one of the tables. The objects in this section provide the ability to focus on a single unit to test.
4+
5+
- [ApplyConstraint](applyconstraint.md)
6+
- [ApplyTrigger](applytrigger.md)
7+
- [FakeFunction](fakefunction.md)
8+
- [FakeTable](faketable.md)
9+
- [RemoveObject](removeobject.md)
10+
- [RemoveObjectIfExists](removeobjectifexists.md)
11+
- [SpyProcedure](spyprocedure.md)

0 commit comments

Comments
 (0)