Skip to content

Commit a49fe6b

Browse files
authored
Add possibility to fake function with temp or derived tables (#53)
* Add possibility to fake function with temp or derived tables * Remove wrongly removed new lines * PR#53 Add new lines at the end of the files * PR#53 Add missing schema for the table * Remove test * PR#53 Fix tests * Change naming convention for the FakeFunction data output table name * Fix issue with the long function names * Add FakeFunction test for long name and output table schema * Add test for data source table starting with SELECT * Using tSQLt.Private::CreateUniqueObjectName() to generate random object name * Remove `FunctionName` parameter * Change build order as CLR is used * Fix fake functions test * Add CLR function exception message test * Remove CONCAT usage * Add CLR TVF support
1 parent b9b9052 commit a49fe6b

6 files changed

+290
-14
lines changed

Source/BuildOrder.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tSQLt.CaptureOutputLog.tbl.sql
2525
tSQLt.LogCapturedOutput.ssp.sql
2626
../Build/temp/CreateAssembly.sql
2727
tSQLtCLR_CreateProcs.sql
28+
tSQLt.Private_PrepareFakeFunctionOutputTable.ssp.sql
2829
tSQLt.TableToText.ssp.sql
2930
tSQLt.Private_RenamedObjectLog.tbl.sql
3031
tSQLt.Private_MarkObjectBeforeRename.ssp.sql

Source/tSQLt.FakeFunction.ssp.sql

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ GO
33
---Build+
44
GO
55
CREATE PROCEDURE tSQLt.FakeFunction
6-
@FunctionName NVARCHAR(MAX),
7-
@FakeFunctionName NVARCHAR(MAX)
6+
@FunctionName NVARCHAR(MAX),
7+
@FakeFunctionName NVARCHAR(MAX) = NULL,
8+
@FakeDataSource NVARCHAR(MAX) = NULL
9+
810
AS
911
BEGIN
1012
DECLARE @FunctionObjectId INT;
@@ -14,6 +16,7 @@ BEGIN
1416
EXEC tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction
1517
@FunctionName = @FunctionName,
1618
@FakeFunctionName = @FakeFunctionName,
19+
@FakeDataSource = @FakeDataSource,
1720
@FunctionObjectId = @FunctionObjectId OUT,
1821
@FakeFunctionObjectId = @FakeFunctionObjectId OUT,
1922
@IsScalarFunction = @IsScalarFunction OUT;
@@ -23,6 +26,7 @@ BEGIN
2326
EXEC tSQLt.Private_CreateFakeFunction
2427
@FunctionName = @FunctionName,
2528
@FakeFunctionName = @FakeFunctionName,
29+
@FakeDataSource = @FakeDataSource,
2630
@FunctionObjectId = @FunctionObjectId,
2731
@FakeFunctionObjectId = @FakeFunctionObjectId,
2832
@IsScalarFunction = @IsScalarFunction;

Source/tSQLt.Private_CreateFakeFunction.ssp.sql

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ GO
33
---Build+
44
GO
55
CREATE PROCEDURE tSQLt.Private_CreateFakeFunction
6-
@FunctionName NVARCHAR(MAX),
7-
@FakeFunctionName NVARCHAR(MAX),
8-
@FunctionObjectId INT,
9-
@FakeFunctionObjectId INT,
10-
@IsScalarFunction BIT
6+
@FunctionName NVARCHAR(MAX),
7+
@FakeFunctionName NVARCHAR(MAX) = NULL,
8+
@FunctionObjectId INT = NULL,
9+
@FakeFunctionObjectId INT = NULL,
10+
@IsScalarFunction BIT = NULL,
11+
@FakeDataSource NVARCHAR(MAX) = NULL
1112
AS
1213
BEGIN
1314
DECLARE @ReturnType NVARCHAR(MAX);
@@ -44,6 +45,12 @@ BEGIN
4445
BEGIN
4546
EXEC('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS '+@ReturnType+' AS BEGIN RETURN '+@FakeFunctionName+'('+@ParameterCallList+');END;');
4647
END
48+
ELSE IF (@FakeDataSource IS NOT NULL)
49+
BEGIN
50+
DECLARE @newTbleName NVARCHAR(MAX);
51+
EXEC tSQLt.Private_PrepareFakeFunctionOutputTable @FakeDataSource, @newTbleName OUTPUT;
52+
EXEC ('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS TABLE AS RETURN ( SELECT * FROM '+@newTbleName+');');
53+
END
4754
ELSE
4855
BEGIN
4956
EXEC('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS TABLE AS RETURN SELECT * FROM '+@FakeFunctionName+'('+@ParameterCallList+');');
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
IF OBJECT_ID('tSQLt.Private_PrepareFakeFunctionOutputTable') IS NOT NULL
2+
DROP PROCEDURE tSQLt.Private_PrepareFakeFunctionOutputTable;
3+
GO
4+
---Build+
5+
CREATE PROCEDURE tSQLt.Private_PrepareFakeFunctionOutputTable
6+
@FakeDataSource NVARCHAR(MAX),
7+
@OutputTable NVARCHAR(MAX) OUTPUT
8+
AS
9+
BEGIN
10+
SET @OutputTable = tSQLt.Private::CreateUniqueObjectName();
11+
12+
IF ( LOWER(LTRIM(@FakeDataSource)) LIKE 'select%'
13+
AND OBJECT_ID(@FakeDataSource) IS NULL
14+
)
15+
BEGIN
16+
SET @FakeDataSource = N'('+ @FakeDataSource + N') a';
17+
END;
18+
19+
DECLARE @Cmd NVARCHAR(MAX) = N'SELECT * INTO ' + @OutputTable + N' FROM ' + @FakeDataSource;
20+
21+
EXEC sp_executesql @Cmd;
22+
23+
RETURN 0;
24+
END;
25+
---Build-
26+
GO

Source/tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction.ssp.sql

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,44 @@ GO
33
---Build+
44
GO
55
CREATE PROCEDURE tSQLt.Private_ValidateObjectsCompatibleWithFakeFunction
6-
@FunctionName NVARCHAR(MAX),
7-
@FakeFunctionName NVARCHAR(MAX),
8-
@FunctionObjectId INT OUTPUT,
9-
@FakeFunctionObjectId INT OUTPUT,
10-
@IsScalarFunction BIT OUTPUT
6+
@FunctionName NVARCHAR(MAX),
7+
@FakeFunctionName NVARCHAR(MAX) = NULL,
8+
@FakeDataSource NVARCHAR(MAX) = NULL,
9+
@FunctionObjectId INT = NULL OUTPUT,
10+
@FakeFunctionObjectId INT = NULL OUTPUT,
11+
@IsScalarFunction BIT = NULL OUTPUT
1112
AS
1213
BEGIN
1314
SET @FunctionObjectId = OBJECT_ID(@FunctionName);
14-
SET @FakeFunctionObjectId = OBJECT_ID(@FakeFunctionName);
1515

1616
IF(@FunctionObjectId IS NULL)
1717
BEGIN
1818
RAISERROR('%s does not exist!',16,10,@FunctionName);
1919
END;
20+
21+
IF COALESCE(@FakeFunctionName, @FakeDataSource) IS NULL
22+
BEGIN
23+
RAISERROR ('Either @FakeFunctionName or @FakeDataSource must be provided', 16, 10);
24+
END;
25+
26+
IF (@FakeFunctionName IS NOT NULL AND @FakeDataSource IS NOT NULL )
27+
BEGIN
28+
RAISERROR ('Both @FakeFunctionName and @FakeDataSource are valued. Please use only one.', 16, 10);
29+
END;
30+
31+
IF (@FakeDataSource IS NOT NULL )
32+
BEGIN
33+
IF NOT EXISTS (
34+
SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(@FunctionName) and type in ('TF', 'IF', 'FT')
35+
)
36+
BEGIN
37+
RAISERROR('You can use @FakeDataSource only with Inline, Multi-Statement or CLR Table-Valued functions.', 16, 10);
38+
END
39+
40+
RETURN 0;
41+
END
42+
43+
SET @FakeFunctionObjectId = OBJECT_ID(@FakeFunctionName);
2044
IF(@FakeFunctionObjectId IS NULL)
2145
BEGIN
2246
RAISERROR('%s does not exist!',16,10,@FakeFunctionName);
@@ -50,4 +74,3 @@ BEGIN
5074
END;
5175
END;
5276
GO
53-

Tests/FakeFunctionTests.class.sql

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,156 @@ BEGIN
422422
EXEC FakeFunctionTests.[assert parameter missmatch causes error];
423423
END;
424424
GO
425+
CREATE PROCEDURE FakeFunctionTests.[test errors when both @FakeFunctionName and @FakeDataSource are passed]
426+
AS
427+
BEGIN
428+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int, @b int) RETURNS TABLE AS RETURN (SELECT @a AS one);');
429+
EXEC tSQLt.ExpectException @ExpectedMessage = 'Both @FakeFunctionName and @FakeDataSource are valued. Please use only one.', @ExpectedSeverity = 16, @ExpectedState = 10;
430+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeFunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'select 1 one';
431+
END;
432+
GO
433+
CREATE PROCEDURE FakeFunctionTests.[test errors when neither @FakeFunctionName nor @FakeDataSource are passed]
434+
AS
435+
BEGIN
436+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int, @b int) RETURNS TABLE AS RETURN (SELECT @a AS one);');
437+
EXEC tSQLt.ExpectException @ExpectedMessage = 'Either @FakeFunctionName or @FakeDataSource must be provided', @ExpectedSeverity = 16, @ExpectedState = 10;
438+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction';
439+
END;
440+
GO
441+
CREATE PROCEDURE FakeFunctionTests.[test errors if function is a SVF and @FakeDataSource is used]
442+
AS
443+
BEGIN
444+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS NVARCHAR(10) AS BEGIN RETURN ''''; END;');
445+
446+
EXEC tSQLt.ExpectException @ExpectedMessage = 'You can use @FakeDataSource only with Inline, Multi-Statement or CLR Table-Valued functions.',
447+
@ExpectedSeverity = 16,
448+
@ExpectedState = 10;
449+
450+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'select 1 as a';
451+
452+
END;
453+
GO
454+
CREATE PROCEDURE FakeFunctionTests.[test errors if function is a CLR SVF and @FakeDataSource is used]
455+
AS
456+
BEGIN
457+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS NVARCHAR(10) AS BEGIN RETURN ''''; END;');
458+
459+
EXEC tSQLt.ExpectException @ExpectedMessage = 'You can use @FakeDataSource only with Inline, Multi-Statement or CLR Table-Valued functions.',
460+
@ExpectedSeverity = 16,
461+
@ExpectedState = 10;
462+
463+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'select 1 as a';
464+
465+
END;
466+
GO
467+
CREATE PROCEDURE FakeFunctionTests.[test can fake Inline table function using a temp table as fake data source]
468+
AS
469+
BEGIN
470+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 1 AS one);');
471+
472+
CREATE TABLE #expected (a CHAR(1));
473+
INSERT INTO #expected VALUES('a');
474+
475+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
476+
477+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction();
478+
479+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
480+
END;
481+
GO
482+
CREATE PROCEDURE FakeFunctionTests.[test can fake multi-statement table function using a temp table as fake data source]
483+
AS
484+
BEGIN
485+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS @t TABLE (a int) AS BEGIN;
486+
INSERT INTO @t (a) VALUES (0) RETURN; END;');
487+
488+
CREATE TABLE #expected (a CHAR(1));
489+
INSERT INTO #expected VALUES('a');
490+
491+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
492+
493+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
494+
495+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
496+
END;
497+
GO
498+
CREATE PROCEDURE FakeFunctionTests.[test can fake CLR table function using a temp table as fake data source]
499+
AS
500+
BEGIN
501+
502+
CREATE TABLE #expected (a CHAR(1));
503+
INSERT INTO #expected VALUES('a');
504+
505+
EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt_testutil.AClrTvf', @FakeDataSource = '#expected';
506+
507+
SELECT * INTO #actual FROM tSQLt_testutil.AClrTvf('', '');
508+
509+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
510+
END;
511+
GO
512+
CREATE PROCEDURE FakeFunctionTests.[test can fake function with one parameter]
513+
AS
514+
BEGIN
515+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS TABLE AS RETURN (SELECT @a AS one);');
516+
517+
CREATE TABLE #expected (a INT);
518+
INSERT INTO #expected VALUES(1);
519+
520+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
521+
522+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
523+
524+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
525+
END;
526+
GO
527+
CREATE PROCEDURE FakeFunctionTests.[test can fake function with multiple parameters]
528+
AS
529+
BEGIN
530+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int, @b int, @c char(1)) RETURNS TABLE AS RETURN (SELECT @a AS one);');
531+
532+
CREATE TABLE #expected (a INT);
533+
INSERT INTO #expected VALUES(1);
534+
535+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
536+
537+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123, 321, 'a');
538+
539+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
540+
END;
541+
GO
542+
CREATE PROCEDURE FakeFunctionTests.[test can fake function with VALUES clause as fake data source]
543+
AS
544+
BEGIN
545+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 777 AS a);');
546+
547+
CREATE TABLE #expected (a INT);
548+
INSERT INTO #expected VALUES(1);
549+
550+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '(VALUES(1)) a(a)';
551+
552+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction();
553+
554+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
555+
END;
556+
GO
557+
CREATE PROCEDURE FakeFunctionTests.[test can fake function with two part named table as fake data source]
558+
AS
559+
BEGIN
560+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 777 AS a);');
561+
562+
CREATE TABLE #expected (a INT);
563+
INSERT INTO #expected VALUES(1);
564+
565+
SELECT * INTO FakeFunctionTests.SomeTable FROM #expected;
566+
567+
568+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'FakeFunctionTests.SomeTable';
569+
570+
SELECT * INTO #actual FROM FakeFunctionTests.AFunction();
571+
572+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
573+
END;
574+
GO
425575
CREATE TYPE FakeFunctionTests.TableType1001 AS TABLE(SomeInt INT);
426576
GO
427577
CREATE PROCEDURE FakeFunctionTests.[test can fake function with table-type parameter]
@@ -442,4 +592,69 @@ BEGIN
442592
EXEC tSQLt.AssertEqualsString @Expected = 233515, @Actual = @Actual;
443593
END;
444594
GO
595+
CREATE PROCEDURE FakeFunctionTests.[test can fake with derived table]
596+
AS
597+
BEGIN
598+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 777 AS a);');
599+
600+
CREATE TABLE #expected (a INT);
601+
INSERT INTO #expected VALUES(1);
602+
603+
CREATE TABLE #actual (a INT)
604+
605+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'select 1 as a';
606+
607+
INSERT INTO #actual SELECT * FROM FakeFunctionTests.AFunction();
608+
609+
EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
610+
END;
611+
GO
612+
CREATE PROCEDURE FakeFunctionTests.[test can fake with data source table that starts with select]
613+
AS
614+
BEGIN
615+
DECLARE @Expected INT = 1;
616+
DECLARE @Actual INT = 0;
617+
618+
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 777 AS a);');
619+
620+
CREATE TABLE SelectFakeFunctionTestsTable (a int);
621+
INSERT INTO SelectFakeFunctionTestsTable VALUES (@Expected);
622+
623+
EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction',
624+
@FakeDataSource = N'SelectFakeFunctionTestsTable';
625+
626+
SELECT @Actual = a FROM FakeFunctionTests.AFunction()
627+
EXEC tSQLt.AssertEquals @Expected, @Actual;
628+
629+
END;
630+
GO
631+
CREATE PROCEDURE FakeFunctionTests.[test Private_PrepareFakeFunctionOutputTable returns table with VALUES]
632+
AS
633+
BEGIN
634+
635+
DECLARE @NewTable sysname;
445636

637+
CREATE TABLE #Expected (a int);
638+
INSERT INTO #Expected VALUES(1);
639+
640+
EXEC tSQLt.Private_PrepareFakeFunctionOutputTable '(VALUES (1)) a(a)', @NewTable OUTPUT;
641+
642+
EXEC tSQLt.AssertEqualsTable '#Expected', @NewTable;
643+
644+
END;
645+
GO
646+
CREATE PROCEDURE FakeFunctionTests.[test Private_PrepareFakeFunctionOutputTable returns table with SELECT query]
647+
AS
648+
BEGIN
649+
650+
DECLARE @NewTable sysname;
651+
652+
CREATE TABLE #Expected (a int);
653+
INSERT INTO #Expected VALUES(1);
654+
655+
EXEC tSQLt.Private_PrepareFakeFunctionOutputTable 'SELECT 1 AS a', @NewTable OUTPUT;
656+
657+
EXEC tSQLt.AssertEqualsTable '#Expected', @NewTable;
658+
659+
END;
660+
GO

0 commit comments

Comments
 (0)