sql >> Base de Datos >  >> RDS >> Database

Saltar para comenzar el desarrollo de bases de datos basadas en pruebas (TDDD)

Por regla general, comenzamos a desarrollar soluciones de base de datos mediante la creación de objetos de base de datos, como tablas, vistas, procedimientos almacenados, etc., en función de los requisitos comerciales. Este enfoque también se conoce como Desarrollo de bases de datos convencionales. . En este artículo, vamos a explorar este enfoque y lo ilustraremos con ejemplos.

Desarrollo de bases de datos convencionales

El estilo de desarrollo consta de los siguientes pasos:

  1. Recibir los requisitos
  2. Cree objetos de base de datos según los requisitos
  3. Ejecute pruebas unitarias para los objetos de la base de datos para ver si cumplen con los requisitos
  4. Recibir nuevos requisitos
  5. Modifique los objetos de base de datos existentes o agregue otros nuevos para cumplir con los nuevos requisitos
  6. Cree y ejecute pruebas unitarias para comprobar si los nuevos requisitos funcionan correctamente y no entran en conflicto con los anteriores

Para explorar e ilustrar los procesos, comencemos configurando una base de datos de muestra SQLDevBlog :

 
-- Create sample database (SQLDevBlog)
  CREATE DATABASE SQLDevBlog

Use el siguiente código para crear tablas en esa base de datos de muestra:

USE SQLDevBlog;

-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

Revise el diagrama de la base de datos con nuestras tablas recién creadas:

Nota :Estoy usando aquí dbForge Studio para SQL Server para hacer todas las tareas. El aspecto de su salida puede diferir de SSMS (SQL Server Management Studio), pero los resultados son los mismos.

A continuación, completaremos nuestro SQLDevBlog base de datos de muestra para crear un escenario más realista:

-- (5) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Sam', '2017-01-01', 'Database Analyst'),
  ('Asif', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sadaf', '2018-01-01', 'Database Analyst Programmer')

-- (6) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')

-- (7) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Fundamentals of SQL Database Development', '02-01-2018', ''),
  (1, 2, 'Advanced Database Development', '02-01-2018', ''),
  (2, 3, 'All About Database Testing', '03-01-2018', '');
GO

Como resultado, tenemos las siguientes tablas pobladas:

Ahora que hemos terminado con la configuración de la base de datos y el llenado de la tabla, nos enfrentamos al siguiente paso. Tenemos que imitar el escenario con un nuevo requisito.

El requisito de agregar una nueva categoría

Un nuevo requisito establece que un administrador debe poder agregar una nueva categoría a la lista de categorías disponibles . Para cumplir con este requisito, su equipo de desarrollo debe crear un procedimiento almacenado para agregar un nuevo requisito con facilidad. O bien, tenemos que crear un AddCategory Objeto de base de datos.

Para crear el procedimiento almacenado, ejecute el siguiente script:

-- (8) This procedure meets a new requirement by adding a new category
CREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS
  INSERT INTO Category (Name, Notes)
    VALUES (@CategoryName, @Notes);
GO

El resultado es el siguiente:

Crear prueba unitaria de base de datos para verificar si el procedimiento funciona correctamente

El siguiente paso es crear la prueba unitaria de la base de datos para verificar si el procedimiento almacenado cumple con la especificación.

Este consejo funciona para dbForge Studio para SQL Server (o solo Prueba unitaria de dbForge ) y SSMS (SQL Server Management Studio) . Nota:cuando utilice SSMS (SQL Server Management Studio), asegúrese de instalar tSQLt Framework para escribir las pruebas unitarias.

Para crear la primera prueba unitaria de la base de datos, haga clic con el botón derecho en SQLDevBlog base de datos> Prueba unitaria > Añadir nueva prueba

El Agregar nueva prueba se abre la ventana. Complete toda la información requerida y haga clic en Agregar prueba .

Cree la prueba unitaria de la siguiente manera y guárdela:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE AddCategoryTests.[test to check if AddCategory procedure works]
AS
BEGIN
  --Assemble
  EXEC tSQLt.FakeTable @TableName = 'dbo.Category' -- create an empty dependency free Category table
                    

  
  CREATE TABLE AddCategoryTests.Expected ( -- create expected table 
  CategoryId INT 
 ,Name VARCHAR(50) NULL
 ,Notes VARCHAR(400) NULL
  ) 
                      
  INSERT INTO AddCategoryTests.Expected (CategoryId,Name, Notes) -- Insert data into expected table
  VALUES (null,'Database Dummy Category', 'This is just a dummy category for testing');
  
  --Act
  EXEC AddCategory @CategoryName = 'Database Dummy Category' 
                  ,@Notes = 'This is just a dummay category for testing'

   
  --Assert
  EXEC tSQLt.AssertEqualsTable @Expected = 'AddCategoryTests.Expected'
                              ,@Actual = 'dbo.Category'
                           

END;
GO

Haga clic en Base de datos menú> Prueba de unidad > Ver lista de pruebas y ejecute la prueba unitaria como se muestra a continuación:

Podemos ver que la prueba unitaria es exitosa. Por lo tanto, se puede agregar una nueva categoría a la base de datos (tabla). El requisito ha sido satisfecho.

Ahora, exploraremos el desarrollo de bases de datos basadas en pruebas y describiremos cómo el proceso de escribir pruebas unitarias puede satisfacer los requisitos.

Desarrollo de base de datos basado en pruebas (TDDD)

El desarrollo de bases de datos basadas en pruebas (TDDD) comienza con la escritura de la prueba unitaria que fallará primero. Luego, lo modificaremos para aprobar y luego lo refinaremos.

Las pruebas unitarias se escriben para cumplir con los requisitos y las pruebas unitarias que requieren que los objetos de la base de datos se creen y ejecuten correctamente.

Para comprender la diferencia entre el desarrollo de bases de datos tradicionales y el desarrollo de bases de datos basado en pruebas, creemos el SQLDevBlogTDD base de datos. Es lo mismo que SQLDevBlog .

-- Create sample database (SQLDevBlogTDD)
  CREATE DATABASE SQLDevBlogTDD

Luego, complete la base de datos de muestra con tablas:

USE SQLDevBlogTDD;

-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

Tenemos que llenar nuestra base de datos de muestra para crear un escenario más realista de la siguiente manera:

-- Use SQLDevBlogTDD
-- (5) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Sam', '2017-01-01', 'Database Analyst'),
  ('Asif', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sadaf', '2018-01-01', 'Database Analyst Programmer')

-- (6) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')

-- (7) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Fundamentals of SQL Database Development', '02-01-2018', ''),
  (1, 2, 'Advanced Database Development', '02-01-2018', ''),
  (2, 3, 'All About Database Testing', '03-01-2018', '');
GO

Requisito para agregar una nueva categoría (TDDD)

Ahora, tenemos el mismo requisito:el administrador debería poder agregar una nueva categoría a la lista de categorías disponibles. Para cumplir con el requisito, primero debemos escribir una prueba de unidad de base de datos que busque un objeto potencial.

Así es como funciona TDDD:la prueba unitaria primero falla ya que asumimos que estamos buscando el objeto que actualmente no existe, pero pronto estará allí.

Cree y ejecute la prueba unitaria de la base de datos para comprobar que existe el objeto deseado

Aunque sabemos que ahora no existe, pensamos en esto como un punto de partida.

En dbForge Studio para SQL Server, la prueba unitaria de la base de datos que admite TDDD de manera predeterminada se crea para fallar primero. Entonces, vamos a cambiarlo un poco. Si está utilizando un tSQLt Marco de prueba de unidad de base de datos directamente, escriba la siguiente prueba de unidad:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE CategoryTests.[test to check if routine to add new category exists]
AS
BEGIN
  --Assemble
  --  This section is for code that sets up the environment. It often
  --  contains calls to methods such as tSQLt.FakeTable and tSQLt.SpyProcedure
  --  along with INSERTs of relevant data.
  --  For more information, see http://tsqlt.org/user-guide/isolating-dependencies/

  --Act
  --  Execute the code under tests like a stored procedure, function, or view
  --  and capture the results in variables or tables.

  --Assert
  --  Compare the expected and actual values, or call tSQLt.Fail in an IF statement.
  --  Available Asserts: tSQLt.AssertEquals, tSQLt.AssertEqualsString, tSQLt.AssertEqualsTable
  --  For a complete list, see: http://tsqlt.org/user-guide/assertions/
  EXEC tSQLt.AssertObjectExists @ObjectName = N'dbo.AddCategory'
                              

END;
GO

Después de ejecutar la prueba unitaria de la base de datos, puede ver que la prueba falla:

Cree el objeto de la base de datos y vuelva a ejecutar la prueba unitaria

El siguiente paso es crear el objeto de base de datos requerido. En nuestro caso, es un procedimiento almacenado.

-- (8) This procedure meets the new requirement by adding a new category
CREATE PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS  
-- Category Procedure Stub (template) in TDDD
GO

Vuelva a ejecutar la prueba unitaria; esta vez tiene éxito:

Pero no es suficiente pasar la prueba unitaria comprobando si existe el procedimiento almacenado. También debemos verificar si el procedimiento almacenado agrega una nueva categoría.

Cree la prueba unitaria de la base de datos para verificar si la rutina funciona correctamente

Vamos a crear la nueva prueba unitaria de la base de datos:

--  Comments here are associated with the test.
--  For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/
CREATE PROCEDURE CategoryTests.[test to check routine adds new category]
AS
BEGIN
  --Assemble
  EXEC tSQLt.FakeTable @TableName = 'dbo.Category' -- create an empty dependency free Category table
                      

  
  CREATE TABLE CategoryTests.Expected ( -- create expected table 
  CategoryId INT 
 ,Name VARCHAR(50) NULL
 ,Notes VARCHAR(400) NULL
  ) 
                      
  INSERT INTO CategoryTests.Expected (CategoryId,Name, Notes) -- Insert data into expected table
  VALUES (null,'Database Dummy Category', 'This is just a dummy category for testing');
  
  --Act
  EXEC AddCategory @CategoryName = 'Database Dummy Category' 
                  ,@Notes = 'This is just a dummay category for testing'

  --SELECT * INTO CategoryTests.Actual FROM Category -- put category table data into an actual table
  
  --Assert
  EXEC tSQLt.AssertEqualsTable @Expected = 'CategoryTests.Expected'
                              ,@Actual = 'dbo.Category'
                           

END;
GO

Como puede ver, la prueba unitaria falla por primera vez y tiene éxito por segunda vez:

Agregar funcionalidad a la rutina y volver a ejecutar la prueba unitaria

Modifique el procedimiento almacenado agregando la funcionalidad requerida para que la prueba pueda tener éxito como se muestra a continuación:

-- (8) This procedure meets the new requirement by adding a new category
ALTER PROCEDURE dbo.AddCategory @CategoryName VARCHAR(50),
@Notes VARCHAR(400)
AS
  INSERT INTO Category (Name, Notes)
    VALUES (@CategoryName, @Notes);
GO

Vuelva a ejecutar las pruebas unitarias para verificar que todas tengan éxito, incluido el procedimiento almacenado modificado recientemente:

De esta manera, hemos implementado con éxito el desarrollo de bases de datos basadas en pruebas. Ahora podemos centrarnos solo en los requisitos. Las pruebas unitarias encapsulan los requisitos, por lo que exigen que los objetos de la base de datos se creen y ejecuten correctamente para cumplir con la especificación.

Veamos qué tan efectivo es TDDD cuando se trata de satisfacer un requisito de informes comerciales.

Satisfacer el requisito de informes comerciales a través de TDDD

Suponemos que la base de datos ya obtuvo los objetos necesarios (como tablas) antes de recibir el nuevo requisito de informes comerciales.

Vamos a crear una base de datos de muestra llamada SQLDevBlogReportTDD :

-- Create sample database (SQLDevBlogReportTDD)
CREATE DATABASE SQLDevBlogReportTDD;
GO

Luego cree y llene las tablas para la base de datos de muestra usando el siguiente código:

USE SQLDevBlogReportTDD;
-- (1) Create Author table in the sample database
CREATE TABLE Author (
  AuthorId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(40)
 ,RegistrationDate DATETIME2
 ,Notes VARCHAR(400)
)

-- (2) Create an Article Category table in the sample database
CREATE TABLE Category (
  CategoryId INT PRIMARY KEY IDENTITY (1, 1)
 ,Name VARCHAR(50)
 ,Notes VARCHAR(400)
)

-- (3) Create Article table in the sample database
CREATE TABLE Article (
  ArticleId INT PRIMARY KEY IDENTITY (1, 1)
 ,CategoryId INT
 ,AuthorId INT
 ,Title VARCHAR(150)
 ,Published DATETIME2
 ,Notes VARCHAR(400)  
)

-- Adding foreign keys for author and article category
ALTER TABLE Article ADD CONSTRAINT FK_Category_CategoryId FOREIGN KEY (CategoryId) REFERENCES Category (CategoryId)
ALTER TABLE Article ADD CONSTRAINT FK_Author_AuthorId FOREIGN KEY (AuthorId) REFERENCES Author (AuthorId)

GO

-- (4) Populating Author table
INSERT INTO Author (Name, RegistrationDate, Notes)
  VALUES ('Peter', '2017-01-01', 'Database Analyst'),
  ('Adil', '2017-01-02', 'Database and Business Intelligence Developer'),
  ('Sarah', '2018-01-01', 'Database Analyst Programmer'),
  ('Asim', '2018-01-01', 'Database Analyst')

-- (5) Populating Category table
INSERT INTO Category (Name, Notes)
  VALUES 
  ('Analysis', 'Database Analysis'),
  ('Development', 'Articles about database development'),
  ('Testing', 'Database testing related articles'),
  ('DLM', 'Database lifecycle management')
 

-- (6) Populating Article 
INSERT INTO Article (CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1, 1, 'Replicating a problem in SQL', '02-01-2018', ''),
  (1, 2, 'Modern Database Development Tools', '02-01-2018', ''),
  (3, 3, 'Test Driven Database Development (TDDD)', '03-01-2018', ''),
  (3, 1, 'Database Unit Testing Fundamentals', '10-01-2018', ''),
  (3, 3, 'Unit Testing with tSQLt', '10-01-2018', '')
GO

Cree una vista para ver la lista de todos los autores, artículos y categorías de artículos:

-- (7) Create a view to see a list of authors, articles, and categories
CREATE VIEW dbo.vwAuthors 
AS SELECT a.Name AS AuthorName,a1.Title AS ArticleTitle,c.Name AS CategoryName  FROM Author a INNER JOIN Article a1 ON a.AuthorId = a1.AuthorId INNER JOIN Category c ON a1.CategoryId = c.CategoryId
GO

Ejecute la vista de la tabla creada para ver los resultados:

Después de procesar la configuración de la base de datos y completar las tablas, el siguiente paso es imitar el escenario en el que recibimos un nuevo requisito.

Requisito comercial:número total de artículos por informe de autor

Considere un nuevo requisito de informes comerciales. Tiene que indicar un informe de base de datos para ver el número total de artículos por autor.

Lo primero es asignar un objeto de base de datos que pueda cumplir con los requisitos comerciales. En nuestro caso, es el ArtículosPerAuthorReport objeto de base de datos.

Para cumplir con el requisito, es necesario crear una prueba de unidad de base de datos que busque un objeto apropiado potencial. Como sabemos, esta prueba fallará primero porque buscará el objeto que no existe en este momento pero que estará allí pronto.

Plan de implementación de TDDD

De acuerdo con los estándares de las pruebas unitarias de bases de datos basadas en pruebas, debe haber lo siguiente para cumplir con el requisito de informes:

  1. Desarrolle un único objeto de base de datos que cumpla con el requisito de generación de informes.
  2. Cree una prueba unitaria para verificar la existencia del objeto.
  3. Cree un objeto stub (marcador de posición) para pasar la primera prueba.
  4. Cree una segunda prueba de unidad para verificar si el objeto genera datos correctos en la tabla con la entrada correcta.
  5. Modifique la definición del objeto para permitir que pase la segunda prueba.

Cree la prueba unitaria de la base de datos para comprobar que existe el objeto deseado

Asignaremos un objeto de base de datos que pueda cumplir con los requisitos comerciales. En nuestro caso, es el ArtículosPerAuthorReport objeto de base de datos. El siguiente paso es crear una prueba de unidad de base de datos.

Para crear la primera prueba unitaria de la base de datos, haga clic con el botón derecho en SQLDevBlogReport base de datos> Prueba unitaria > Añadir nueva prueba

Escriba el siguiente código para crear la prueba unitaria que compruebe la existencia o ausencia del objeto deseado:

CREATE PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport exists]
AS
BEGIN
  --Assemble
 
  --Act
  
  --Assert
   EXEC tSQLt.AssertObjectExists @ObjectName = N'ArticlesPerAuthorReport'
END;
GO

La prueba unitaria debe fallar ya que verifica el objeto creado antes. El objeto en sí se crea para cumplir con TDDD:

Crear stub de objeto y ejecutar la unidad

Cree un objeto auxiliar con algún resultado esperado codificado de forma rígida, ya que solo queremos crear un objeto de base de datos con el resultado esperado. Cree el informe de artículos por autor objeto como un fragmento de vista (marcador de posición) al principio:

-- (8) Create ArticlesPerAuthorReport view stub
  CREATE VIEW ArticlesPerAuthorReport
    AS
    SELECT 'Adil' AS Author, 10 AS [Total Articles]
    UNION ALL
    SELECT 'Sam' AS Author, 5 AS [Total Articles]

La ejecución de la prueba unitaria debería tener éxito:

Crear un stub sirve como puntapié inicial para TDDD. Creamos el objeto para pasar la prueba y no nos preocupamos por el funcionamiento real del objeto.

Cree y ejecute la prueba unitaria para verificar si el objeto genera datos correctos

Es hora de comprobar si el objeto deseado está funcionando correctamente. Cree otra prueba unitaria para verificar la salida de datos por el objeto deseado (ArtículosPerAuthorReport ). Agreguemos una nueva prueba unitaria a la base de datos:

Agregue el siguiente código de prueba de unidad:

CREATE PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport outputs correct]
AS
BEGIN
  --Assemble
  --  Create mocked up tables (blank copies of original tables without constraints and data)
  EXEC tSQLt.FakeTable @TableName = N'Author'
                      ,@SchemaName = N'dbo'

  EXEC tSQLt.FakeTable @TableName = N'Article'
                      ,@SchemaName = N'dbo'                      

  EXEC tSQLt.FakeTable @TableName = N'Category'
                      ,@SchemaName = N'dbo'                      

  -- Add rows to the mocked up tables
  INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)
  VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),
    (2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')
  
  INSERT INTO Category (CategoryID,Name, Notes)
  VALUES (1,'Database Development', '-'),
  (2,'Business Intelligene','-');

  INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),
  (1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),
  (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,2, 2, 'Tabular Models', DATEFROMPARTS(2017,02,01),'50K Views')

  -- Create an expected table
  CREATE TABLE ArticlesPerAuthorReport.Expected
  (Author VARCHAR(40),[Total Articles] int)  

  -- Add expected results into an expected table
  INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Total Articles])
  VALUES ('Zak', 3), ('Akeel',2);


  --Act
  --  Run ArticlesPerAuthorReport object (view) and put results into an actual table
  SELECT * INTO ArticlesPerAuthorReport.Actual FROM ArticlesPerAuthorReport apar
  

  --Assert
  --  Compare the expected and actual tables
  EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorReport.Expected'
                              ,@Actual = N'ArticlesPerAuthorReport.Actual'
                              

END;
GO

Ejecute la prueba unitaria que también debe fallar para cumplir con TDDD:

Agregue la funcionalidad requerida al objeto ArticlesPerAuthorReport

La prueba unitaria que verifica la funcionalidad del objeto requiere una estructura modificada para que la prueba pueda pasar.

Modificar el ArtículosPerAuthorReport view para permitir que la prueba de la segunda unidad pase igual que la primera:

ALTER VIEW ArticlesPerAuthorReport
  AS
SELECT a.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles] FROM Author a 
    INNER JOIN Article a1 ON a.AuthorId = a1.AuthorId
    GROUP BY a.Name

El objeto de la base de datos se ha modificado correctamente para generar los datos deseados. Ejecute todas las pruebas unitarias:

El informe de artículos por autor el objeto está listo.

Nuestra próxima tarea es proporcionar un recorrido por la creación de un informe basado en un objeto de base de datos desarrollado y probado mediante el uso de desarrollo basado en pruebas (TDDD).

Implementación del requisito de informes (Artículos por informe de autor)

Primero, vamos a restablecer el SQLDevBlogReportTDD y agregarle más datos. O bien, puede crear una base de datos en blanco por primera vez.

Para agregar suficientes datos a nuestra base de datos de muestra, recupere datos de vwAuthors view para ver todos los registros:

Ejecutar las pruebas unitarias

Ejecute las pruebas unitarias de la base de datos que creamos anteriormente:

Felicitaciones, ambas pruebas han pasado, lo que significa que el objeto de base de datos deseado es capaz de cumplir con el requisito de informes para ver los artículos por autor.

Crear informe de base de datos basado en el objeto ArticlesPerAuthorsReport

Puede crear un informe de base de datos de muchas formas (por ejemplo, mediante el Generador de informes, la creación de un Proyecto de servidor de informes en Visual Studio Data Tools o el uso de dbForge Studio para SQL Server).

En esta sección, estamos utilizando dbForge Studio para SQL Server para la creación de informes. Para continuar, haga clic en Nuevo del Archivo Menú> Informe de datos :

Haga clic en Informe estándar :

Seleccione Tabla simple\Vista como Tipo de datos :

Agregue el objeto base (ArtículosPerAuthorReport ), que es una vista en nuestro caso:

Agregue los campos obligatorios:

No necesitamos ninguna agrupación en este momento, así que continúe haciendo clic en Siguiente :

Seleccione Diseño y Orientación del informe de datos:

Finalmente, agregue el título Artículos por informe de autor y haga clic en Finalizar :

A continuación, ajuste el formato del informe según los requisitos:

Haga clic en Vista previa para ver el informe de la base de datos:

Guarde el informe como Reporte de artículos por autor . El informe de la base de datos se ha creado debido a los requisitos comerciales.

Uso del procedimiento de configuración

Cuando escribe pruebas de unidad de base de datos usando tSQLt, verá que algunos códigos de prueba se repiten con frecuencia. Por lo tanto, puede definirlo en un procedimiento de configuración y reutilizarlo luego en otras pruebas unitarias de esa clase de prueba en particular. Cada clase de prueba puede tener solo un procedimiento de configuración que se ejecuta automáticamente antes de que se procesen las pruebas unitarias de esa clase.

Podemos poner casi todo el código de prueba escrito en Ensamblar (sección) en el procedimiento de configuración para evitar la duplicación de código.

Por ejemplo, necesitamos crear tablas simuladas junto con una tabla esperada. Luego agregaremos datos a las tablas simuladas y luego a la tabla esperada. Podemos definirlo fácilmente en el procedimiento de configuración y reutilizarlo más.

Creación de un procedimiento de configuración para evitar la duplicación del código de prueba

Cree un procedimiento almacenado en SQLDevBlogTDD de la siguiente manera:

-- (12) Use of Setup Procedure to avoid repeating common test code
CREATE PROCEDURE ArticlesPerAuthorReport.Setup 
AS 
BEGIN
  --Assemble
  --  Create mocked up tables (blank copies of original tables without constraints and data)
  EXEC tSQLt.FakeTable @TableName = N'Author'
                      ,@SchemaName = N'dbo'

  EXEC tSQLt.FakeTable @TableName = N'Article'
                      ,@SchemaName = N'dbo'                      

  EXEC tSQLt.FakeTable @TableName = N'Category'
                      ,@SchemaName = N'dbo'                      

  -- Add rows to the mocked up tables
  INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)
  VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),
    (2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')
  
  INSERT INTO Category (CategoryID,Name, Notes)
  VALUES (1,'Database Development', '-'),
  (2,'Business Intelligene','-');

  INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)
  VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),
  (1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),
  (1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2017,02,01),'10K Views'),
  (1,2, 2, 'Tabular Models', DATEFROMPARTS(2017,02,01),'50K Views')

  -- Create an expected table
  CREATE TABLE ArticlesPerAuthorReport.Expected
  (Author VARCHAR(40),[Total Articles] int)  

  -- Add expected results into an expected table
  INSERT INTO ArticlesPerAuthorReport.Expected (Author, [Total Articles])
  VALUES ('Zak', 3), ('Akeel',2)
END;
GO

Ahora, elimine el código de prueba que hemos escrito en el procedimiento de configuración de la prueba unitaria anterior para verificar Artículos por informe de autor salidas de la siguiente manera:

-- (11) Create unit test check ArticlesPerAuthorReport outputs correct
ALTER PROCEDURE ArticlesPerAuthorReport.[test to check ArticlesPerAuthorReport outputs correct]
AS
BEGIN
  --Assemble (Test Code written in Setup Procedure)
  -- Create mocked up tables (blank copies of original tables without constraints and data)
  -- Add rows to the mocked up tables
  -- Create an expected table
  -- Add expected results into an expected table
  
  --Act
  --  Run ArticlesPerAuthorReport object (view) and put results into an actual table
  SELECT * INTO ArticlesPerAuthorReport.Actual FROM ArticlesPerAuthorReport apar
  

  --Assert
  --  Compare the expected and actual tables
  EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorReport.Expected'
                              ,@Actual = N'ArticlesPerAuthorReport.Actual'
                              
END;
GO

Running All Unit Tests to Check Setup Procedure Working

Run the unit tests and see the results:

The unit tests have run successfully despite the fact we are using a setup procedure to run some parts of the test code before these unit tests are running.

Use of Stored Procedures

Next, we’ll focus on creating stored procedures through test-driven database development (TDDD) to meet specific requirements that cannot be fulfilled by using a database view.

Let’s assume that business users want to know the Total number of articles per author for a specified year . The database view can’t meet it because it is not defined at the time of writing the script (exactly the year is going to be desired by the business users).

Thus, it requires a database object with parameter(s) capability and it is exactly the stored procedure.

Let us consider a new business requirement to create the report that shows the total number of articles per author for a specified year . We’ll use the sample database called SQLDevBlogReportTDD that we created earlier.

Run the ArticlesPerAuthorReport view to see the results:

Select the Database Object (AuthorsPerArticleByYearReport)

Name the potential database object as AuthorsPerArticleForYearReport .

As we mentioned above, the database view can meet the reporting requirement despite the absence of the specified year . But this variable means that we need the stored procedure which will pass year as an argument to run the report and show the desired results.

Write and Run the Object Exists Unit Test

As we already know, we need to start with writing the basic unit test to check the existence or absence of the desired object.

To create the first database unit test, right-click the SQLDevBlogReport database> Unit Test > Add New Test

Write the following test code:

CREATE PROCEDURE ArticlesPerAuthorByYearReport.[test to check ArticlesPerAuthorByYearReport exists]

AS

BEGIN

--Assemble

--Act

--Assert

EXEC tSQLt.AssertObjectExists @ObjectName = N'ArticlesPerAuthorByYearReport'

,@Message = N''


END;

GO

Right-click on the database> click View Test List under Unit Test to see the Test List Manager :

Check the ArticlesPerAuthorByYearReport test class and click the run test icon:

This complies with TDDD – the unit test checking if object existence is written before the object is created. So, we expect the test to fail first.

Create Object Stub (dummy object)

We are going to create an object stub that mocks the object’s functionality. At this stage, we only need that object, the desired functionality is out of the question.

Create a stored procedure type object as the stub and call it ArticlesPerAuthorByYearReport by using the following code:

-- Create report object (stored procedure) stub

CREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport

@Year INT

AS

SELECT 'Adil' AS Author, 10 AS [Total Articles], 0000 AS [Year]

UNION ALL

SELECT 'Sam' AS Author, 5 AS [Total Articles], 0000 AS [Year]

GO

After we created the object stub, the basic unit test that checks for the existence of the object will be successful:

Write and Run the Object Functionality Unit Test

To comply with TDDD, we need to write a unit test to check whether the desired object ArticlesPerAuthorByYearReport functions properly. Since the object was created as a stub (placeholder), this unit test is also going to fail first. The object has to function properly yet despite the fact it was created and passed the basic check of its existence.

Create a second unit test to check if the object outputs correct data by creating a setup procedure (which helps us to write shared test code within the same test class) that is followed by the unit test:

CREATE PROCEDURE ArticlesPerAuthorByYearReport. Setup

AS

BEGIN

--Assemble

-- Create mocked up tables (blank copies of original tables without constraints and data)

EXEC tSQLt.FakeTable @TableName = N'Author'

,@SchemaName = N'dbo'




EXEC tSQLt.FakeTable @TableName = N'Article'

,@SchemaName = N'dbo'




EXEC tSQLt.FakeTable @TableName = N'Category'

,@SchemaName = N'dbo'




-- Add rows to the mocked up tables

INSERT INTO Author (AuthorId,Name, RegistrationDate, Notes)

VALUES (1,'Zak', DATEFROMPARTS(2017,01,01), 'Database Author'),

(2,'Akeel',DATEFROMPARTS(2018,01,01),'Business Intelligence Author')

INSERT INTO Category (CategoryID,Name, Notes)

VALUES (1,'Database Development', '-'),

(2,'Business Intelligene','-');




INSERT INTO Article (ArticleId,CategoryId, AuthorId, Title, Published, Notes)

VALUES (1,1, 1, 'Advanced Database Development', DATEFROMPARTS(2017,02,01),'10K Views'),

(1,1, 1, 'Database Development with Cloud Technologies', DATEFROMPARTS(2017,02,01),'5K Views'),

(1,1, 1, 'Developing Databases with Modern Tools', DATEFROMPARTS(2017,03,01),'20K Views'),

(1,2, 2, 'Business Intelligence Fundamentals', DATEFROMPARTS(2016,02,01),'10K Views'),

(1,2, 2, 'Tabular Models', DATEFROMPARTS(2016,02,01),'50K Views')




-- Create an expected table

CREATE TABLE ArticlesPerAuthorByYearReport.Expected

(Author VARCHAR(40),[Total Articles] INT,[Year] INT)




-- Create an actual table

CREATE TABLE ArticlesPerAuthorByYearReport.Actual

(Author VARCHAR(40),[Total Articles] INT,[Year] INT)




-- Add expected results into an expected table for the year 2017

INSERT INTO ArticlesPerAuthorByYearReport.Expected (Author, [Total Articles],[Year])

VALUES ('Zak', 3,2017)




END;

GO

Write the unit test to check if the object functions properly:

-- Create unit test to check ArticlesPerAuthorByYearReport outputs correct data

CREATE PROCEDURE ArticlesPerAuthorByYearReport.[test to check ArticlesPerAuthorByYearReport outputs correct data]

AS

BEGIN

--Assemble (Test Code written in Setup Procedure)

-- Create mocked up tables (blank copies of original tables without constraints and data)

-- Add rows to the mocked up tables

-- Create an expected table

-- Create an actual table

-- Add expected results into an expected table

--Act

-- Call desired object (stored procedure) and put results into an actual table

INSERT INTO ArticlesPerAuthorByYearReport.Actual

EXEC dbo.ArticlesPerAuthorByYearReport @Year=2017




--Assert

-- Compare the expected and actual tables

EXEC TSQLT.AssertEqualsTable @Expected = N'ArticlesPerAuthorByYearReport.Expected'

,@Actual = N'ArticlesPerAuthorByYearReport.Actual'

END;

GO

Run the unit test. As demonstrated earlier, it will fail first since we have not added the desired functionality to the object yet:

Add Object Functionality and Rerun the Unit Test

Add the object functionality by modifying the stored procedure as follows:

-- Create report object (stored procedure) to show articles per author for a specified year

CREATE PROCEDURE dbo.ArticlesPerAuthorByYearReport

@Year INT

AS




SELECT

a.Name AS [Author],COUNT(a1.ArticleId) AS [Total Articles],YEAR(a.RegistrationDate) AS [Year]

FROM Author a

INNER JOIN Article a1

ON a.AuthorId = a1.AuthorId

WHERE YEAR(a.RegistrationDate) = @Year

GROUP BY a.Name,YEAR(a.RegistrationDate)

GO

Note :If you are using a declarative database development tool like dbForge Studio for SQL Server, you’ll use the Create Procedure statement to modify the object. For tools like SSMS (SQL Server Management Studio), you must use ALTER Procedure .

Rerunning the database unit test for checking the proper object functioning gives us the following results:

You have successfully unit tested the reporting procedure that is responsible for meeting the business requirement.

Conclusión

Test-driven database development (TDDD) is a specific approach. To meet the business requirement(s), potential database object(s) must pass the unit test(s) and satisfy the following conditions under normal circumstances:

  • The database object must exist
  • The database object must function properly to meet the business requirement

First, the unit tests have to fail because they are created before the creation of the object/defining the object functionality. After adding the necessary objects and ensuring their functionality, the unit tests succeed.

This article examined the basics of test-driven database development and illustrated it with practical examples. We hope that the article was helpful to you. Feel free to share your opinions and maybe some lifehacks in the Comments section, and stay tuned for the next materials!