Este artigo fornece um passo a passo da unidade de banco de dados testando um procedimento armazenado que contém um procedimento utilitário dentro dele.
Neste artigo, discutirei um cenário de teste de unidade de banco de dados quando um procedimento armazenado principal depende de um procedimento de utilitário e o procedimento principal precisa ser testado em unidade para garantir que os requisitos sejam atendidos. A chave é garantir que um teste de unidade só possa ser escrito para uma única unidade de código, o que significa que precisamos de um teste de unidade para o procedimento principal e outro teste de unidade para o procedimento utilitário.
O teste de unidade de um único procedimento armazenado é mais fácil em comparação com o teste de unidade de um procedimento que chama um procedimento de utilitário dentro de seu código.
É muito importante entender o cenário do procedimento do utilitário e por que ele é diferente do teste de unidade de um procedimento armazenado normal.
Cenário:Procedimento de Utilidade dentro do Procedimento Principal
Para entender o cenário do procedimento de utilidade, vamos começar pela definição e exemplo de procedimento de utilidade:
O que é Procedimento de Utilidade
Um procedimento utilitário é geralmente um pequeno procedimento usado pelo(s) procedimento(s) principal(is) para realizar alguma tarefa específica, como obter algo para o procedimento principal ou adicionar algo ao procedimento principal.
Outra definição de procedimento utilitário é um pequeno procedimento armazenado escrito para fins de manutenção que pode envolver tabelas ou visualizações do sistema a serem chamadas por qualquer número de procedimentos ou mesmo diretamente.
Exemplos de procedimento de utilidade
Pense em um cenário de pedido de cliente-produto em que um cliente faz um pedido para um determinado produto. Se criarmos o procedimento principal para obter todos os pedidos feitos por um determinado cliente, um procedimento utilitário pode ser usado para nos ajudar a entender se cada pedido foi feito pelo cliente no dia da semana ou no fim de semana.
Desta forma, um pequeno procedimento de utilidade pode ser escrito para retornar “Dia da semana” ou “Fim de semana” com base na data em que o produto foi pedido pelo cliente.
Outro exemplo pode ser procedimentos armazenados no sistema, como “sp_server_info” no banco de dados mestre, que fornece informações sobre a versão instalada do SQL Server:
EXEC sys.sp_server_info
Por que o procedimento do utilitário de teste de unidade é diferente
Conforme discutido anteriormente, o teste de unidade de um procedimento utilitário que é chamado dentro do procedimento principal é um pouco mais complicado do que o teste de unidade de um procedimento armazenado simples.
Considerando o exemplo do produto do pedido do cliente mencionado acima, precisamos escrever um teste de unidade para verificar se o procedimento do utilitário está funcionando bem e também um teste de unidade deve ser escrito para verificar se o procedimento principal que chama o procedimento do utilitário também está funcionando corretamente, além de atender o(s) requisito(s) de negócios.
Isso é ilustrado a seguir:
Isolando do Desafio de Utilidade/Procedimento Principal
O principal desafio ao escrever um(s) teste(s) de unidade para o procedimento que envolve um procedimento de utilidade é ter certeza de que não devemos nos preocupar com o funcionamento do procedimento de utilidade ao escrever um teste de unidade para o procedimento principal e o mesmo vale para o procedimento de utilidade . Esta é uma tarefa desafiadora que deve ser mantida em mente ao escrever testes de unidade para tal cenário.
Isolar do utilitário ou procedimento principal é uma obrigação, dependendo de qual procedimento está sendo testado. Temos que manter as seguintes coisas em mente no contexto de isolamento durante o teste de unidade:
- Isolando do procedimento utilitário ao testar a unidade do procedimento principal.
- Isolando do procedimento principal quando o procedimento do utilitário de teste de unidade.
Lembre-se de que este artigo se concentra no teste de unidade do procedimento principal, isolando-o de seu procedimento utilitário.
Criando Procedimento Principal e seu Procedimento Utilitário
Para escrever um teste de unidade para um cenário em que o procedimento utilitário está em uso pelo procedimento principal, primeiro precisamos ter os seguintes pré-requisitos:
- Banco de dados de amostra
- Requisitos comerciais
Configurar banco de dados de exemplo (SQLBookShop)
Estamos criando um banco de dados simples de duas tabelas chamado “SQLBookShop” que contém os registros de todos os livros ordenados conforme mostrado abaixo:
Crie o banco de dados de amostra SQLBookShop da seguinte maneira:
-- (1) Create SQLBookShop database CREATE DATABASE SQLBookShop; GO
Crie e preencha objetos de banco de dados (tabelas) da seguinte forma:
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
Verificação Rápida – Banco de Dados de Amostra
Faça uma verificação rápida do banco de dados executando a visualização OrderedBooks usando o seguinte código:
USE SQLBookShop -- Run OrderedBooks view SELECT ob.OrderID ,ob.OrderDate ,ob.Title AS BookTitle ,ob.Quantity ,ob.TotalPrice FROM dbo.OrderedBooks ob
Observe que estou usando o dbForge Studio para SQL Server, portanto, a aparência da saída pode ser diferente se você executar o mesmo código no SSMS (SQL Server Management Studio). No entanto, não há diferença entre os scripts e seus resultados.
Requisito comercial para ver o pedido mais recente com informações adicionais
Um requisito de negócios foi enviado à equipe de desenvolvimento informando que "O usuário final deseja saber sobre o pedido mais recente feito para um livro específico, juntamente com as informações se o pedido foi feito em um dia de semana ou fim de semana"
Uma palavra sobre TDDD
Não estamos seguindo estritamente o desenvolvimento de banco de dados orientado a testes (TDDD) neste artigo, mas recomendo fortemente usar o desenvolvimento de banco de dados orientado a testes (TDDD) para criar procedimentos principais e utilitários que começam criando um teste de unidade para verificar se o objeto existe. falha no início, seguido pela criação do objeto e reexecução do teste de unidade que deve passar.
Para um passo a passo detalhado, consulte a primeira parte deste artigo.
Identificando o Procedimento de Utilidade
Vendo o requisito de negócios, uma coisa é certa:precisamos de um procedimento utilitário que possa nos dizer se uma determinada data é um dia da semana ou um fim de semana.
Procedimento de criação de utilitário (GetDayType)
Crie um procedimento utilitário e chame-o de “GetDayType” da seguinte forma:
-- 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
Verificação Rápida - Procedimento de Utilidade
Escreva as seguintes linhas de código para verificar rapidamente o procedimento do utilitário:
-- Quick check utility procedure declare @DayType varchar(10) EXEC uspGetDayType '20181001',@DayType output select @DayType AS [Type of Day]
Criando procedimento principal (GetLatestOrderByBookId)
Crie o procedimento principal para ver o pedido mais recente feito para um determinado livro e também se o pedido foi feito em um dia da semana ou fim de semana e chame-o de “GetLatestOrderByBookId” que contém a chamada para o procedimento utilitário da seguinte forma:
-- 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
Verificação Rápida – Procedimento Principal
Execute o seguinte código para ver se o procedimento está funcionando bem ou não:
-- Get latest order for the bookid=6 EXEC uspGetLatestOrderByBookId @BookId = 6
Procedimento principal de teste de unidade Procedimento de chamada do utilitário
A chave aqui é entender a diferença entre o teste de unidade do procedimento principal e o procedimento utilitário.
No momento, estamos focados em testar a unidade do procedimento principal, portanto, isso significa que o procedimento utilitário precisa ser isolado desse teste de unidade.
Uso do procedimento de espionagem
Para garantir que o teste de unidade do procedimento principal permaneça focado em testar a funcionalidade do procedimento principal, temos que usar o procedimento spy fornecido pelo tSQLt que atuará como um stub (espaço reservado) para o procedimento utilitário.
De acordo com tsqlt.org, por favor, lembre-se que se você estiver espionando um procedimento, você não está realmente testando a unidade desse procedimento, mas sim tornando mais fácil para o outro procedimento relacionado ao procedimento que você está espionando ser testado por unidade.
Por exemplo, no nosso caso, se quisermos fazer o teste unitário do procedimento principal, teremos que zombar do procedimento utilitário usando o procedimento spy, o que facilitará o teste unitário do procedimento principal.
Criação de teste de unidade para o procedimento do utilitário de espionagem do procedimento principal
Crie um teste de unidade de banco de dados para verificar as funções do procedimento principal corretamente.
Este artigo funciona para dbForge Studio para SQL Server (ou apenas dbForge Unit Test) e SSMS (SQL Server Management Studio) . No entanto, observe que, ao usar o SSMS (SQL Server Management Studio), presumo que você já tenha instalado o tSQLt Framework e esteja pronto para escrever os testes de unidade.
Para criar o primeiro teste de unidade de banco de dados, clique com o botão direito do mouse no banco de dados SQLBookShop. No menu de atalho, clique em Unit Test e, em seguida, Add New Test da seguinte forma:
Escreva o código do teste de unidade:
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
Executando o teste de unidade para o procedimento principal
Execute o teste de unidade:
Parabéns, você testou com sucesso um procedimento armazenado isolando-o de seu procedimento utilitário após usar o procedimento de espionagem.
Para obter mais informações sobre testes de unidade, consulte as seguintes partes do meu artigo anterior sobre desenvolvimento de banco de dados orientado a testes (TDDD):
- Ir para o desenvolvimento de banco de dados orientado a testes (TDDD) – parte 1
- Ir para o desenvolvimento de banco de dados orientado a testes (TDDD) – parte 2
- Ir para o desenvolvimento de banco de dados orientado a testes (TDDD) – parte 3
Coisas para fazer
Agora você pode criar testes de unidade de banco de dados para cenários ligeiramente complexos em que procedimentos armazenados chamam procedimentos de utilitário.
- Por favor, tente alterar o procedimento de espionagem @CommandToExecute argumento (valor) como @CommandToExecute =‘set @DayType =”Nothing” ‘ e veja que o teste vai falhar agora
- Tente atender ao requisito comercial neste artigo usando o desenvolvimento de banco de dados orientado a testes (TDDD)
- Tente atender a outro requisito comercial para ver o pedido mais recente feito por qualquer cliente usando desenvolvimento orientado a testes (TDDD) envolvendo o mesmo procedimento de utilidade
- Tente criar um teste de unidade para o procedimento utilitário isolando o procedimento principal
- Por favor, tente criar um teste de unidade para um procedimento que chama dois procedimentos utilitários
Ferramenta útil:
dbForge Unit Test – uma GUI intuitiva e conveniente para implementar testes de unidade automatizados no SQL Server Management Studio.