[SQL]介紹tSQLt的常用Function

  • 276
  • 0

tSQLt提供了一些功能以進行資料庫的單元測試的管理,也實作了一些可以隔離資料庫物件相依性的功能。底下是這些功能的簡介及一些心得。

 

Test Class的建立與執行

tSQLt提供了幾個功能,以用來建立Test Case。建立Test Case的第一步,就是呼叫tSQLt.NewTestClass以建立一個test class。這個步驟做的事情其實很單純,就是依據傳入的TestClass名稱建立同名的Schema,並且在Schema的定義中加入extended property的註記,以讓tSQLt在執行時可以辨識該Schema。

之後以Stored Procedure的形式建立Test Case,並且使用命名原則來辨識哪些SP是用來進行單元測試。該命名原則就是Stored Procedure的名稱需以test開頭。底下就是一個Test Case的範例。

CREATE PROCEDURE LabHello.[test rate]
AS
BEGIN
    DECLARE @actual MONEY;
    DECLARE @rate DECIMAL(10,4); SET @rate = 1.2;
    DECLARE @amount MONEY; SET @amount = 2.00;

    SELECT @actual = dbo.ConvertCurrency(@rate, @amount);

    DECLARE @expected MONEY; SET @expected = 2.4;   --(rate * amount)
    EXEC tSQLt.AssertEquals @expected, @actual;

END;
GO

當tSQLt進行測試程序時,會找出這些以test開頭的SP,並開啟Transaction。當該SP執行結束時,再將Transaction進行rolled-back,以清掉測試過程中的各種異動。而測試的結果,則會記錄在該database的tSQLt.TestResult這個table中。

以下介紹幾個管理TestClass會用到的功能:

NewTestClass

建立Test Class(Schema)

語法:

tSQLt.NewTestClass [@ClassName = ] 'class name'

RenameClass

修改Test Class的名稱

語法:

tSQLt.RenameClass [@SchemaName = ] 'class name'
                , [@NewSchemaName = ] 'new class name'

DropClass

刪掉Test Class以及屬於該Test Class的物件

語法:

tSQLt.DropClass [@ClassName = ] 'class name'

RunAll

這個指令會執行該database上,所有的test case

語法:

tSQLt.RunAll

Run

這個指令可以依據輸入的參數,執行特定的測試案例

  • 如果是test class的名稱,則執行屬於該test class的所有測試案例
  • 如果是test case名稱,則執行該test case
  • 如果是空白,則執行上一個有參數的Run指令。當有需求要重複測試時,就不需要每次都輸入參數。

語法:

tSQLt.Run [ [@testName = ] 'test name' ]

Assertions

單元測試有所謂的3A原則,tSQLt也提供一系列的功能作為Assert之用。

AssertEmptyTable:

檢驗目標Table的資料行數是否為0 語法:

tSQLt.AssertEmptyTable [@TableName = ] 'name of table to be checked'
                     [, [@FailMsg = ] 'message' ]

AssertEquals

檢驗預期值實際值是否相同 語法:

tSQLt.AssertEquals [@expected = ] expected value
                 , [@actual = ] actual value
                [, [@message = ] 'message' ]

AssertNotEquals

檢驗預期值實際值是否不同

語法:

tSQLt.AssertNotEquals [@expected = ] expected value
                 , [@actual = ] actual value
                [, [@message = ] 'message' ]

AssertEqualsString

檢驗預期字串實際字串是否相同 語法:

tSQLt.AssertEqualsString [@expected = ] expected value
                       , [@actual = ] actual value
                      [, [@message = ] 'message' ]

AssertLike

檢驗預期值實際值是否相同,而預期值可以使用Like的語法表達式。

tSQLt.AssertLike [@ExpectedPattern = ] expected pattern
               , [@Actual = ] actual value
              [, [@Message = ] 'message' ]

範例如下:

EXEC tSQLt.AssertLike 'hello', 'hello'; -- pass
EXEC tSQLt.AssertLike '%el%', 'hello'; - pass
EXEC tSQLt.AssertLike 'h_llo', 'hello'; - pass
EXEC tSQLt.AssertLike '%oo%', 'hello'; - fail

AssertEqualsTable

檢測兩個Table內的資料內容是否相同,如果不同,還會顯示其差異內容

語法:

tSQLt.AssertEqualsTable [@Expected = ] 'expected table name'
                      , [@Actual = ] 'actual table name'
                     [, [@FailMsg = ] 'message' ]

AssertEqualsTableSchema

檢測兩個Table的Schema是否相同

語法:

tSQLt.AssertEqualsTableSchema [@Expected = ] 'expected table name'
                            , [@Actual = ] 'actual table name'
                           [, [@FailMsg = ] 'message' ]                           

AssertObjectExists

檢測資料庫物件是否存在

語法:

tSQLt.AssertObjectExists [@objectName = ] 'object name'
                      [, [@message = ] 'message' ]

AssertObjectDoesNotExist

檢測資料庫物件是否不存在

語法:

tSQLt.AssertObjectDoesNotExist [@objectName = ] 'object name'
                                [, [@message = ] 'message' ]

Fail

直接觸發一個測試錯誤,並可指定錯誤訊息

語法:

tSQLt.Fail [ [@Message0 = ] message part ]
          [, [@Message1 = ] message part ]
          [, [@Message2 = ] message part ]
          [, [@Message3 = ] message part ]
          [, [@Message4 = ] message part ]
          [, [@Message5 = ] message part ]
          [, [@Message6 = ] message part ]
          [, [@Message7 = ] message part ]
          [, [@Message8 = ] message part ]
          [, [@Message9 = ] message part ]

隔離相依性

在單元測試中,會使用stub、mock、fake物件,以達到將測試的目標物件中相依元件隔離開來的目的。在tSQLt的架構中也一樣,tSQLt提供一些功能讓Stored Procedure、Function的相依性減到最低。

FakeTable

這個功能可以依據指定的Table Name建立一個沒有constraints的空Table。並且在執行測試時,直接取代原先的Table。這樣有兩個好處,一來進行測試時不會受到既有的資料的影響,二來塞入模擬資料到Table時比較簡單,因為沒有constraints。

語法

tSQLt.FakeTable [@TableName = ] 'table name'
                , [[@SchemaName = ] 'schema name']
                , [[@Identity = ] 'preserve identity']
                , [[@ComputedColumns = ] 'preserve computed columns']
                , [[@Defaults = ] 'preserve default constraints']

FakeFunction

這個功能是在進行測試時,可以讓一個預先建立的簡單的Function,取代原本實作所有商業邏輯的複雜Function。這是因為如果在測試一個Stored Procedure時,如果會叫用該複雜的Function,有可能該複雜Function邏輯出問題而影響到測試結果。在隔離相依性的需求下,就可以預先建立一個簡單的Function(可能是回傳一個固定值),替換掉原先複雜的Function。

語法:

tSQLt.FakeFunction [@FunctionName = ] 'function name'
                 , [@FakeFunctionName = ] 'fake function name'

SpyProcedure

這個功能與FakeFunction類似,不過是作用在Stored Procedure上。在測試案例中,使用此功能以指定的sql statement替換掉指定的Stored Procedure。同時,它還可以紀錄傳入到Stored Procedure的參數值,到額外建立的Table中,以進行trace及debug之用。

語法:

tSQLt.SpyProcedure [@ProcedureName = ] 'procedure name'
                [, [@CommandToExecute = ] 'command' ]

RemoveObject

在測試案例中,這個功能可以暫時移除指定的資料庫物件(其實是透過更名的方式),然後再以自訂的mock物件取代原物件,已達到隔離隔離相依性的需求。

語法:

tSQLt.RemoveObject [@ObjectName= ] 'object name'
                [, [@NewName = ] 'new object name' OUTPUT]
                [, [@IfExists = ] ( 0 | 1 )]