Bài viết này cung cấp hướng dẫn về cách kiểm tra đơn vị cơ sở dữ liệu một thủ tục được lưu trữ có chứa một thủ tục tiện ích bên trong nó.
Trong bài viết này, tôi sẽ thảo luận về một kịch bản kiểm thử đơn vị cơ sở dữ liệu khi một thủ tục được lưu trữ chính phụ thuộc vào một thủ tục tiện ích và thủ tục chính cần được kiểm thử đơn vị để đảm bảo rằng các yêu cầu được đáp ứng. Điều quan trọng là đảm bảo rằng một bài kiểm tra đơn vị chỉ có thể được viết cho một đơn vị mã, có nghĩa là chúng ta cần một bài kiểm tra đơn vị cho thủ tục chính và một bài kiểm tra đơn vị khác cho thủ tục tiện ích.
Kiểm tra đơn vị một thủ tục được lưu trữ đơn lẻ dễ dàng hơn so với kiểm tra đơn vị một thủ tục gọi một thủ tục tiện ích bên trong mã của nó.
Điều rất quan trọng là phải hiểu kịch bản thủ tục tiện ích và lý do tại sao nó khác với việc kiểm tra đơn vị một thủ tục được lưu trữ thông thường.
Tình huống:Thủ tục Tiện ích trong Quy trình Chính
Để hiểu kịch bản thủ tục tiện ích, chúng ta hãy bắt đầu bằng định nghĩa và ví dụ về thủ tục tiện ích:
Thủ tục Tiện ích là gì
Thủ tục tiện ích nói chung là một thủ tục nhỏ được (các) thủ tục chính sử dụng để thực hiện một số tác vụ cụ thể chẳng hạn như lấy một cái gì đó cho thủ tục chính hoặc thêm một cái gì đó vào thủ tục chính.
Một định nghĩa khác về thủ tục tiện ích là một thủ tục nhỏ được lưu trữ được viết cho mục đích bảo trì có thể liên quan đến các bảng hoặc khung nhìn hệ thống được gọi bằng bất kỳ số lượng thủ tục nào hoặc thậm chí trực tiếp.
Ví dụ về Thủ tục Tiện ích
Hãy nghĩ về tình huống khách hàng đặt hàng sản phẩm trong đó khách hàng đặt hàng cho một sản phẩm cụ thể. Nếu chúng tôi tạo quy trình chính để giúp chúng tôi nhận được tất cả các đơn hàng được đặt bởi một khách hàng cụ thể thì một thủ tục tiện ích có thể được sử dụng để giúp chúng tôi hiểu liệu từng đơn hàng đã được khách hàng đặt vào ngày trong tuần hay cuối tuần.
Bằng cách này, a Quy trình tiện ích nhỏ có thể được viết để trả lại "Ngày trong tuần" hoặc "Ngày cuối tuần" dựa trên ngày sản phẩm được khách hàng đặt hàng.
Một ví dụ khác có thể là các thủ tục được hệ thống lưu trữ chẳng hạn như “sp_server_info” trong cơ sở dữ liệu chính cung cấp thông tin về phiên bản đã cài đặt của SQL Server:
EXEC sys.sp_server_info
Tại sao Quy trình Tiện ích Kiểm tra Đơn vị lại Khác nhau
Như đã thảo luận trước đó, kiểm thử đơn vị một thủ tục tiện ích được gọi bên trong thủ tục chính hơi phức tạp hơn kiểm thử đơn vị một thủ tục lưu trữ đơn giản.
Xét ví dụ về đơn đặt hàng-sản phẩm của khách hàng được đề cập ở trên, chúng ta cần viết một bài kiểm tra đơn vị để kiểm tra thủ tục tiện ích có hoạt động tốt hay không và cũng phải viết một bài kiểm tra đơn vị để kiểm tra thủ tục chính mà gọi thủ tục tiện ích cũng hoạt động bình thường cũng như đáp ứng. (các) yêu cầu kinh doanh.
Điều này được minh họa như sau:
Tách biệt khỏi Tiện ích / Thử thách thủ tục chính
Thách thức chính khi viết (các) kiểm thử đơn vị cho thủ tục liên quan đến thủ tục tiện ích là đảm bảo rằng chúng ta không phải lo lắng về hoạt động của thủ tục tiện ích khi viết kiểm thử đơn vị cho thủ tục chính và điều này cũng đúng với thủ tục tiện ích . Đây là một nhiệm vụ khó khăn cần được ghi nhớ khi viết các bài kiểm tra đơn vị cho một tình huống như vậy.
Việc tách biệt khỏi tiện ích hoặc thủ tục chính là điều bắt buộc, tùy thuộc vào thủ tục nào đang được thử nghiệm. Chúng tôi phải ghi nhớ những điều sau trong bối cảnh cô lập trong khi thử nghiệm đơn vị:
- Tách biệt khỏi thủ tục tiện ích khi đơn vị thử nghiệm thủ tục chính.
- Tách biệt khỏi thủ tục chính khi thủ tục tiện ích thử nghiệm đơn vị.
Hãy nhớ bài viết này tập trung vào việc kiểm tra đơn vị thủ tục chính bằng cách tách nó khỏi quy trình tiện ích của nó.
Tạo Thủ tục Chính và Thủ tục Tiện ích của nó
Để viết một bài kiểm tra đơn vị cho một tình huống trong đó thủ tục tiện ích được sử dụng bởi thủ tục chính, trước tiên chúng ta cần có các điều kiện tiên quyết sau:
- Cơ sở dữ liệu Mẫu
- (Các) Yêu cầu Kinh doanh
Thiết lập cơ sở dữ liệu mẫu (SQLBookShop)
Chúng tôi đang tạo một cơ sở dữ liệu mẫu gồm hai bảng đơn giản có tên “SQLBookShop” chứa các bản ghi của tất cả các sách được đặt hàng như hình dưới đây:
Tạo cơ sở dữ liệu mẫu SQLBookShop như sau:
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Tạo và điền các đối tượng cơ sở dữ liệu (bảng) như sau:
USE SQLBookShop; -- (2) Drop book and book order tables if they already exist IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='BookOrder') DROP TABLE dbo.BookOrder IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Book') DROP TABLE dbo.Book IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_TYPE='View' AND t.TABLE_NAME='OrderedBooks') DROP VIEW dbo.OrderedBooks -- (3) Create book table CREATE TABLE Book (BookId INT PRIMARY KEY IDENTITY(1,1), Title VARCHAR(50), Stock INT, Price DECIMAL(10,2), Notes VARCHAR(200) ) -- (4) Create book order table CREATE TABLE dbo.BookOrder (OrderId INT PRIMARY KEY IDENTITY(1,1), OrderDate DATETIME2, BookId INT, Quantity INT, TotalPrice DECIMAL(10,2) ) -- (5) Adding foreign keys for author and article category ALTER TABLE dbo.BookOrder ADD CONSTRAINT FK_Book_BookId FOREIGN KEY (BookId) REFERENCES Book (BookId) -- (6) Populaing book table INSERT INTO dbo.Book (Title, Stock, Price, Notes) VALUES ('Mastering T-SQL in 30 Days', 10, 200, ''), ('SQL Database Reporting Fundamentals', 5, 100, ''), ('Common SQL Mistakes by Developers',15,100,''), ('Optimising SQL Queries',20,200,''), ('Database Development and Testing Tips',30,50,''), ('Test-Driven Database Development (TDDD)',20,200,'') -- (7) Populating book order table INSERT INTO dbo.BookOrder (OrderDate, BookId, Quantity, TotalPrice) VALUES ('2018-01-01', 1, 2, 400), ('2018-01-02', 2, 2, 200), ('2018-01-03', 3, 2, 200), ('2018-02-04', 1, 2, 400), ('2018-02-05', 1, 3, 600), ('2018-02-06', 4, 3, 600), ('2018-03-07', 5, 2, 100), ('2018-03-08', 6, 2, 400), ('2018-04-10', 5, 2, 100), ('2018-04-11', 6, 3, 600); GO -- (8) Creating database view to see all the books ordered by customers CREATE VIEW dbo.OrderedBooks AS SELECT bo.OrderId ,bo.OrderDate ,b.Title ,bo.Quantity ,bo.TotalPrice FROM BookOrder bo INNER JOIN Book b ON bo.BookId = b.BookId
Kiểm tra nhanh - Cơ sở dữ liệu mẫu
Thực hiện kiểm tra nhanh cơ sở dữ liệu bằng cách chạy chế độ xem Sách có thứ tự sử dụng mã sau:
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Xin lưu ý rằng tôi đang sử dụng dbForge Studio cho SQL Server nên giao diện đầu ra có thể khác nếu bạn chạy cùng một mã trong SSMS (SQL Server Management Studio). Tuy nhiên, không có sự khác biệt giữa các tập lệnh và kết quả của chúng.
Yêu cầu kinh doanh để xem đơn đặt hàng mới nhất với thông tin bổ sung
Một yêu cầu kinh doanh đã được gửi đến nhóm nhà phát triển, trong đó nói rằng “Người dùng cuối muốn biết về đơn đặt hàng gần đây nhất được đặt cho một cuốn sách cụ thể cùng với thông tin liệu đơn đặt hàng được đặt vào ngày trong tuần hay cuối tuần”
Đôi nét về TDDD
Chúng tôi không tuân thủ nghiêm ngặt phát triển cơ sở dữ liệu hướng thử nghiệm (TDDD) trong bài viết này nhưng tôi thực sự khuyên bạn nên sử dụng phát triển cơ sở dữ liệu hướng thử nghiệm (TDDD) để tạo cả thủ tục chính và thủ tục tiện ích, bắt đầu bằng cách tạo thử nghiệm đơn vị để kiểm tra xem đối tượng có tồn tại hay không không thành công lúc đầu, tiếp theo là tạo đối tượng và chạy lại bài kiểm tra đơn vị phải vượt qua.
Để xem hướng dẫn chi tiết, vui lòng tham khảo phần đầu tiên của bài viết này.
Quy trình Xác định Tiện ích
Nhìn thấy yêu cầu kinh doanh, chắc chắn một điều là chúng ta cần một thủ tục tiện ích có thể cho chúng ta biết một ngày cụ thể là ngày trong tuần hay cuối tuần.
Tạo thủ tục tiện ích (GetDayType)
Tạo một thủ tục tiện ích và gọi nó là “GetDayType” như sau:
-- Creating utility procedure to check whether the date passed to it is a weekday or weekend CREATE PROCEDURE dbo.uspGetDayType @OrderDate DATETIME2,@DayType CHAR(7) OUT AS BEGIN SET NOCOUNT ON IF (SELECT DATENAME(WEEKDAY, @OrderDate)) = 'Saturday' OR (SELECT DATENAME(WEEKDAY, @OrderDate)) = 'Sunday' SELECT @DayType= 'Weekend' ELSE SELECT @DayType = 'Weekday' SET NOCOUNT OFF END GO
Kiểm tra nhanh - Thủ tục Tiện ích
Viết các dòng mã sau để nhanh chóng kiểm tra thủ tục tiện ích:
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Tạo thủ tục chính (GetLatestOrderByBookId)
Tạo quy trình chính để xem đơn đặt hàng gần đây nhất được đặt cho một cuốn sách cụ thể và cả đơn đặt hàng được đặt vào ngày trong tuần hay cuối tuần và gọi nó là “GetLatestOrderByBookId” chứa lệnh gọi cho thủ tục tiện ích như sau:
-- Creating stored procedure to get most recent order based on bookid and also whether order was placed on weekend or weekday CREATE PROCEDURE dbo.uspGetLatestOrderByBookId @BookId INT AS BEGIN -- Declare variables to store values DECLARE @OrderId INT ,@Book VARCHAR(50) ,@OrderDate DATETIME2 ,@Quantity INT ,@TotalPrice DECIMAL(10, 2) ,@DayType VARCHAR(10) -- Get most recent order for a particular book and initialise variables SELECT TOP 1 @OrderId = bo.OrderId ,@Book = b.Title ,@OrderDate = bo.OrderDate ,@Quantity = bo.Quantity ,@TotalPrice = bo.TotalPrice FROM BookOrder bo INNER JOIN Book b ON bo.BookId = b.BookId WHERE bo.BookId = @BookId ORDER BY OrderDate DESC -- Call utility procedure to get type of day for the above selected most recent order EXEC uspGetDayType @OrderDate ,@DayType OUTPUT -- Show most recent order for a particular book along with the information whether order was placed on weekday or weekend SELECT @OrderId AS OrderId ,@OrderDate AS OrderDate ,@Book AS Book ,@Quantity AS Quantity ,@TotalPrice AS TotalPrice ,@DayType AS DayType END GO
Kiểm tra nhanh - Quy trình chính
Chạy mã sau để xem liệu quy trình có hoạt động tốt hay không:
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Quy trình chính Kiểm tra đơn vị Quy trình gọi tiện ích
Chìa khóa ở đây là hiểu sự khác biệt giữa quy trình chính và thủ tục kiểm tra đơn vị.
Chúng tôi hiện đang tập trung vào kiểm tra đơn vị thủ tục chính, vì vậy, điều này có nghĩa là quy trình tiện ích cần được tách biệt một cách khéo léo với kiểm thử đơn vị này.
Sử dụng Thủ tục Gián điệp
Để đảm bảo rằng kiểm tra đơn vị thủ tục chính vẫn tập trung vào kiểm tra chức năng của thủ tục chính, chúng tôi phải sử dụng thủ tục gián điệp do tSQLt cung cấp, thủ tục này sẽ hoạt động như một sơ khai (trình giữ chỗ) cho thủ tục tiện ích.
Theo tsqlt.org, hãy nhớ rằng nếu bạn đang theo dõi một thủ tục, bạn không thực sự kiểm tra đơn vị thủ tục đó thay vì bạn đang làm cho thủ tục khác liên quan đến quy trình mà bạn đang gián điệp được kiểm tra đơn vị dễ dàng hơn.
Ví dụ:trong trường hợp của chúng ta, nếu chúng ta muốn kiểm tra đơn vị thủ tục chính thì chúng ta phải mô phỏng thủ tục tiện ích bằng cách sử dụng thủ tục gián điệp, điều này sẽ giúp chúng ta kiểm tra đơn vị thủ tục chính dễ dàng hơn.
Tạo đơn vị kiểm tra cho quy trình chính Thủ tục gián điệp tiện ích
Tạo một bài kiểm tra đơn vị cơ sở dữ liệu để kiểm tra các chức năng của thủ tục chính một cách chính xác.
Bài viết này phù hợp với dbForge Studio dành cho SQL Server (hoặc chỉ Kiểm tra đơn vị dbForge) và SSMS (SQL Server Management Studio) . Tuy nhiên, xin lưu ý rằng khi sử dụng SSMS (SQL Server Management Studio), tôi giả sử bạn đã cài đặt tSQLt Framework và sẵn sàng viết các bài kiểm tra đơn vị.
Để tạo bài kiểm tra đơn vị cơ sở dữ liệu đầu tiên, bấm chuột phải vào cơ sở dữ liệu SQLBookShop. Trên menu lối tắt, bấm Kiểm tra Đơn vị và sau đó Thêm Kiểm tra Mới như sau:
Viết mã kiểm tra đơn vị:
CREATE PROCEDURE GetLatestOrder.[test to check uspGetLatestOrderByBookId outputs correct data] AS BEGIN --Assemble -- Mock order Book and BookOrder table EXEC tSQLt.FakeTable @TableName='dbo.Book' EXEC tSQLt.FakeTable @TableName='dbo.BookOrder' -- Adding mock data to book table INSERT INTO dbo.Book (BookId,Title, Stock, Price, Notes) VALUES (1,'Basics of T-SQL Programming', 10, 100, ''), (2,'Advanced T-SQL Programming', 10, 200, '') -- Adding mock data to bookorder table INSERT INTO dbo.BookOrder (OrderId,OrderDate, BookId, Quantity, TotalPrice) VALUES (1,'2018-01-01', 1, 2, 200), (2,'2018-05-01', 1, 2, 200), (3,'2018-07-01', 2, 2, 400) -- Creating expected table CREATE TABLE GetLatestOrder.Expected ( OrderId INT ,OrderDate DATETIME2 ,Book VARCHAR(50) ,Quantity INT ,TotalPrice DECIMAL(10, 2) ,DayType VARCHAR(10) ) -- Creating actual table CREATE TABLE GetLatestOrder.Actual ( OrderId INT ,OrderDate DATETIME2 ,Book VARCHAR(50) ,Quantity INT ,TotalPrice DECIMAL(10, 2) ,DayType VARCHAR(10) ) -- Creating uspGetDayType spy procedure to isolate main procedure from it so that main procedure can be unit tested EXEC tSQLt.SpyProcedure @ProcedureName = 'dbo.uspGetDayType',@CommandToExecute = 'set @DayType = ''Weekday'' ' -- Inserting expected values to the expected table INSERT INTO GetLatestOrder.Expected (OrderId, OrderDate, Book, Quantity, TotalPrice, DayType) VALUES (2,'2018-05-01', 'Basics of T-SQL Programming', 2, 200,'Weekday'); --Act INSERT INTO GetLatestOrder.Actual EXEC uspGetLatestOrderByBookId @BookId = 1 -- Calling the main procedure --Assert --Compare expected results with actual table results EXEC tSQLt.AssertEqualsTable @Expected = N'GetLatestOrder.Expected', -- nvarchar(max) @Actual = N'GetLatestOrder.Actual' -- nvarchar(max) END; GO
Kiểm tra Đơn vị Chạy cho Quy trình Chính
Chạy thử nghiệm đơn vị:
Xin chúc mừng, bạn đã kiểm tra đơn vị thành công một quy trình được lưu trữ bằng cách cô lập nó khỏi quy trình tiện ích của nó sau khi sử dụng quy trình gián điệp.
Để biết thêm thông tin về kiểm thử đơn vị, vui lòng xem qua các phần sau của bài viết trước của tôi về phát triển cơ sở dữ liệu hướng kiểm tra (TDDD):
- Bước tới Bắt đầu Phát triển Cơ sở dữ liệu Theo hướng Thử nghiệm (TDDD) - Phần 1
- Bước tới Bắt đầu Phát triển Cơ sở dữ liệu Theo hướng Thử nghiệm (TDDD) - Phần 2
- Bước tới Bắt đầu Phát triển Cơ sở dữ liệu Theo hướng Thử nghiệm (TDDD) - Phần 3
Việc cần làm
Giờ đây, bạn có thể tạo các bài kiểm tra đơn vị cơ sở dữ liệu cho các tình huống hơi phức tạp trong đó các thủ tục được lưu trữ gọi các thủ tục tiện ích.
- Vui lòng thử thay đổi quy trình gián điệp @CommandToExecute đối số (giá trị) thành @CommandToExecute =‘set @DayType =” Nothing ”‘ và kiểm tra sẽ thất bại ngay bây giờ
- Vui lòng thử đáp ứng yêu cầu kinh doanh trong bài viết này bằng cách sử dụng phát triển cơ sở dữ liệu theo hướng thử nghiệm (TDDD)
- Vui lòng thử đáp ứng một yêu cầu kinh doanh khác để xem đơn đặt hàng gần đây nhất được đặt bởi bất kỳ khách hàng nào bằng cách sử dụng phát triển theo hướng thử nghiệm (TDDD) liên quan đến cùng một quy trình tiện ích
- Vui lòng thử tạo một bài kiểm tra đơn vị cho quy trình tiện ích bằng cách tách riêng quy trình chính
- Hãy thử tự tạo một bài kiểm tra đơn vị cho một thủ tục gọi hai thủ tục tiện ích
Công cụ hữu ích:
dbForge Unit Test - một GUI trực quan và thuận tiện để triển khai kiểm thử đơn vị tự động trong SQL Server Management Studio.