FYI: Este artigo é uma versão expandida do meu relatório nos SQA Days # 25.Com base na minha experiência de comunicação com colegas, posso dizer que testar código em um banco de dados não é uma prática comum. Isso pode ser um risco potencial. A lógica no banco de dados é escrita pelas mesmas pessoas que escrevem o código "regular". Consequentemente, os erros também podem estar presentes lá e também podem ter consequências negativas para o produto, negócios e consumidores. Não importa se é sobre procedimentos armazenados que ajudam o back-end ou sobre ETLs que convertem dados em armazenamento - existe um risco e os testes podem reduzi-los significativamente. Quero lhe dizer o que é o tSQLt e como ele nos ajuda a testar o código no SQL Server.
Contexto
Há um grande armazém no SQL Server contendo vários dados de pesquisa clínica. Ele é preenchido de várias fontes (principalmente bancos de dados orientados a documentos). Dentro do próprio servidor, os dados são convertidos repetidamente usando ETL. No futuro, esses dados poderão ser carregados em bancos de dados menores para uso em aplicativos da web que resolvem alguns pequenos problemas específicos. Alguns dos clientes do cliente também solicitam uma API para suas necessidades internas. Na implementação dessas APIs, procedimentos e consultas armazenados são frequentemente usados.
Em geral, o código está no lado do DBMS em ordem.
Por que tudo isso é necessário
Como já entendido na introdução, o código no banco de dados é o mesmo código
aplicativos, como o restante, e também pode haver erros.
Eu acho que muitas pessoas estão cientes da dependência do preço do bug no momento de sua descoberta, cuja descoberta é geralmente atribuída a Barry Bohem. Um erro cometido em um estágio inicial e detectado em um estágio posterior pode ser mais caro devido à necessidade de passar por vários estágios (codificação, unidade, integração, teste do sistema etc.) repetidamente, tanto para localizar o erro quanto para trazer de volta o código corrigido. o estágio em que o problema foi identificado. Esse efeito também é relevante para o caso do armazém. Se um erro for arrastado para algum ETL e os dados sofrerem várias transformações, se um erro for detectado nos dados, você deverá:
- Siga todas as etapas da conversão de volta para a localização do problema.
- Corrija o problema.
- Recupere os dados corrigidos (as correções manuais não são excluídas).
- Verifique se os dados incorretos causados pelo erro não apareceram em outro lugar.
Não esqueça que não vendemos brinquedos macios. Um erro em um campo como a pesquisa clínica pode causar danos não apenas aos negócios, mas também à saúde das pessoas.
Como testar?
Como estamos falando de teste de código, queremos dizer teste de unidade e integração. Essas coisas são muito ensaiadas e envolvem regressão constante. A rigor, ninguém realiza esses testes manualmente (bem, talvez com exceção de alguns casos completamente degenerados).
Um bom bônus: os testes podem ser um material auxiliar ao documentar o código. A propósito, os requisitos do cliente podem ser assim (clicáveis):
Excel, duas colunas com requisitos + informações de suporte dispersas em outras colunas + marcação arrastada, o que é mais confuso do que útil. Se necessário, restaurar os desejos originais pode ser difícil. Os testes podem ajudar a capturar com mais precisão as nuances da implementação (é claro, você não deve considerá-las como o equivalente da documentação).
Infelizmente, com a complexidade do código, a complexidade dos testes aumenta e esse efeito pode ser nivelado.
Os testes podem servir como uma camada adicional de segurança contra morsas espontâneas. Os autotestes no IC devido ao formalismo ajudam a lidar com esse problema.
Se nossa escolha caiu no caminho da automação, precisamos decidir sobre as ferramentas para sua implementação.
Como testar?
No caso de testar o código no banco de dados, distingo duas abordagens: alimentado por SQL, ou seja, funcionando diretamente no DBMS e alimentado por não-SQL. Pude destacar as seguintes nuances:
No SQL Server, temos algumas opções:
Avaliações de "bom-mau" são subjetivas, desculpe, sem isso, em lugar nenhum.
Explicação: “Primeira Aparição” é a data mais antiga no caminho da vida da estrutura que eu consegui encontrar, ou seja, a liberação ou consolidação mais antiga.
Você pode notar que as alternativas baseadas em SQL foram abandonadas por algum tempo e o tSQLt é a única opção suportada. Funcionalmente, o tSQLt também vence. A única coisa é que, em termos de asserções, o TST possui uma opção um pouco mais rica que o tSQLt, que, no entanto, dificilmente superará seus contras.
Existem nuances na documentação do tSQLt, mas falarei sobre isso mais tarde.
No mundo que não usa SQL, as coisas não são tão diretas. Alternativas, embora não super ativas, estão sendo desenvolvidas. DbFit é uma ferramenta interessante baseada na estrutura FitNesse. Ele oferece testes de escrita na marcação wiki. O Slacker também é uma coisa curiosa - a abordagem do BDD ao escrever testes para o banco de dados.
Vou discutir as asserções em não-SQL-powered. Externamente, há menos deles, e pode-se dizer que eles são piores por causa disso. Mas aqui vale a pena considerar que eles são fundamentalmente diferentes do tSQLt. Nem tudo é tão simples.
A última linha é "NUnit, etc." - é um lembrete. Muitas das estruturas de teste de unidade conhecidas no trabalho cotidiano podem ser usadas em bancos de dados auxiliares usando bibliotecas auxiliares. A tabela possui muito N / A, porque essa linha, de fato, inclui muitas ferramentas. As “nuances” na coluna de asserções vêm do mesmo ponto - em diferentes ferramentas, seu conjunto pode variar e a questão da aplicabilidade ao banco de dados é aberta.
Como outra métrica interessante, podemos considerar as
tendências do Google .
Nuances:
- Não incluí o Slacker, porque esse nome pode significar muitas coisas (e solicitações como "estrutura do Slacker" não são particularmente visíveis nos gráficos).
- Por uma questão de curiosidade, acrescentou a tendência do TST, mas também não reflete muito o estado das coisas, pois é uma abreviação que significa muitas coisas diferentes.
- Não incluí o NUnit e seus análogos, pois eles eram originalmente estruturas para testar o código dos aplicativos, e suas tendências não são indicativas do nosso contexto.
Em geral, podemos dizer que o tSQLt parece favorável no contexto de análogos.
O que é tSQLt?
O tSQLt, como você pode imaginar, é uma estrutura de teste de unidade baseada em SQL.
→
Site oficialO suporte ao SQL Server foi anunciado desde 2005 SP2. Eu nunca fui capaz de olhar tão longe no passado, mas temos 2012 no servidor de desenvolvimento, localmente em 2017 - não houve problemas.
Código aberto, uma licença do Apache 2.0,
está disponível no GitHub . Você pode bifurcar, contrabandear, usar gratuitamente em projetos comerciais e, o mais importante, não ter medo de indicadores no CLR.
Mecânica do trabalho
Casos de teste são procedimentos armazenados. Eles são combinados em classes de teste (suíte de testes em termos de xUnit).
As classes de teste nada mais são do que esquemas regulares de banco de dados. Eles diferem de outros esquemas pelo registro nas tabelas de estrutura. Você pode fazer esse registro chamando um procedimento - tSQLt.NewTestClass.
Dentro da classe de teste, também é possível definir um procedimento SetUp que será executado antes de cada caso de teste individual ser executado.
Não é necessário um procedimento de desmontagem para restaurar o sistema após a conclusão do caso de teste. Cada caso de teste, juntamente com o procedimento SetUp, é executado em uma transação separada, que é revertida após a coleta dos resultados. Isso é muito conveniente, mas tem alguns efeitos negativos, que discutimos abaixo.
A estrutura permite executar casos de teste individuais, classes de teste inteiras ou todas as classes de teste registradas ao mesmo tempo.
Recursos por exemplo
Não tendo o desejo de recontar um guia oficial já simples, falarei sobre as possibilidades da estrutura usando exemplos.
Isenção de responsabilidade:- exemplos são simplificados;
- no original, nem todos os códigos de teste foram escritos por mim, são os frutos da criatividade coletiva;
- O Exemplo 2 foi inventado para demonstrar mais completamente os recursos do tSQLt.
Exemplo 1: CsvSql
A pedido de um dos clientes do cliente, o seguinte foi implementado. O banco de dados nos campos Nvarchar (MAX) armazena consultas SQL. Para visualizar essas consultas, um frontend mínimo é parafusado. Os conjuntos de resultados retornados por essas solicitações são usados pelo back-end para gerar o arquivo CSV para retornar por meio da chamada da API.
Os conjuntos de resultados são bastante pesados e contêm muitas colunas. Um exemplo condicional de um conjunto de resultados:
Este conjunto de resultados é alguns dados de ensaios clínicos. Vamos dar uma olhada em como o ClinicsNum é considerado - o número de clínicas envolvidas no estudo. Temos duas tabelas: [Trial] e [Clinic]:
Existe FK: [Clinic]. [TrialID] -> [Trial]. [TrialID]. Obviamente, para calcular o número de clínicas, precisamos apenas da COUNT usual (*).
SELECT COUNT(*), ... FROM dbo.Trial LEFT JOIN dbo.Clinic ON Trial.ID = Clinic.TrialID WHERE Trial.Name = @trialName GROUP BY ...
Como testamos essa solicitação? Para começar, podemos usar o conveniente esboço - FakeTable, que simplificará muito o trabalho adicional.
EXEC tSQLt.FakeTable 'dbo.Trial'; EXEC tSQLt.FakeTable 'dbo.Clinic';
O FakeTable faz uma coisa simples - renomeia tabelas antigas e cria novas em seu lugar. Os mesmos nomes, as mesmas colunas, mas sem constrangimento'ov e disparo'ov.
Por que precisamos disso:
- Pode haver alguns dados no banco de dados de teste que podem interferir nos testes. Graças ao FakeTable, não somos dependentes deles.
- Para o teste, como regra, você precisa preencher apenas algumas colunas. Pode haver muitos deles na tabela, e alguns deles terão restrições. Dessa maneira, simplificamos a instalação adicional dos dados de teste - inseriremos apenas aqueles realmente necessários para o caso de teste.
- Definitivamente, não afetamos nenhum gatilho ao inserir dados de teste e não precisamos nos preocupar com pós-efeitos indesejados.
Em seguida, insira os dados de teste que precisamos:
INSERT INTO dbo.Trial ([ID], [Name]) VALUES (1, 'Valerian'); INSERT INTO dbo.Clinic ([ID], [TrialID], [Name]) VALUES (1, 1, 'Clinic1'), (2, 1, 'Clinic2');
Nós obtemos nossa consulta do banco de dados, criamos a tabela Actual e a preenchemos com o conjunto de resultados da nossa consulta:
DECLARE @sqlStatement NVARCHAR(MAX) = (SELECT… CREATE TABLE actual ([TrialID], ...); INSERT INTO actual EXEC sp_executesql @sqlStatement, ...
Preenchimento esperado - valores esperados:
CREATE TABLE expected ( ClinicsNum INT ); INSERT INTO expected SELECT 2
Quero chamar sua atenção para o fato de que, na tabela Esperado, temos apenas uma coluna, enquanto que em Real, temos um conjunto completo.
Isso se deve ao recurso do procedimento AssertEqualsTable, que usaremos para verificar os valores.
EXEC tSQLt.AssertEqualsTable 'expected', 'actual', 'incorrect number of clinics';
Ele compara apenas as colunas que estão presentes nas duas tabelas sendo comparadas. Isso é muito conveniente no nosso caso, pois a consulta de teste retorna muitas colunas, cada uma com sua própria lógica, às vezes muito confusa. Não queremos aumentar os casos de teste, portanto esse recurso é muito útil para nós. Claro, esta é uma faca de dois gumes. Se em Real um conjunto de colunas for preenchido automaticamente através de SELECT TOP 0 e, em algum momento, uma coluna extra aparecer de repente, esse caso de teste não será capturado neste momento. Para lidar com essas situações, você precisa fazer verificações adicionais.
Procedimentos da irmã AssertEqualsTable
Vale ressaltar que o tSQLt contém dois procedimentos semelhantes ao AssertEqualsTable. Estes são AssertEqualsTableSchema e AssertResultSetsHaveSameMetaData. O primeiro faz o mesmo que AssertEqualsTable, mas nos metadados da tabela. O segundo faz uma comparação semelhante, mas nos metadados dos conjuntos de resultados.
Exemplo 2: Restrições
No exemplo anterior, vimos como você pode remover as restrições. Mas e se precisarmos verificar? Tecnicamente, isso também faz parte da lógica e também pode ser considerado um candidato à cobertura do teste.
Considere a situação do exemplo anterior. Duas tabelas - [Trial] e [Clinic]; [TrialID] FK:
Vamos tentar escrever um caso de teste para testar essa restrição. A princípio, como da última vez, falsificamos mesas.
EXEC tSQLt.FakeTable '[dbo].[Trial]' EXEC tSQLt.FakeTable '[dbo].[Clinic]'
O objetivo é o mesmo - livrar-se de restrições desnecessárias. Queremos verificações isoladas, sem gestos desnecessários.
Em seguida, retornamos a restrição necessária para o local usando o procedimento ApplyConstraint:
EXEC tSQLt.ApplyConstraint '[dbo].[Clinic]', 'Trial_FK';
Aqui reunimos uma configuração conveniente para verificação direta. A verificação propriamente dita consistirá no fato de que uma tentativa de inserir dados levará inevitavelmente a uma exceção. Para que o caso de teste funcione corretamente, precisamos capturar essa mesma exceção. O manipulador de exceção ExpectException nos ajudará com isso.
EXEC tSQLt.ExpectException @ExpectedMessage = 'The INSERT statement conflicted...', @ExpectedSeverity = 16, @ExpectedState = 0;
Após instalar o manipulador, você pode tentar inserir o não inserível.
INSERT INTO [dbo].[Clinic] ([TrialID]) VALUES (1)
Exceção capturada. Teste aprovado.
Procedimentos para irmãs ApplyConstraint
Os desenvolvedores do TSQLt nos oferecem uma abordagem semelhante para testar gatilhos. Você pode usar o procedimento ApplyTrigger para retornar um gatilho para a tabela falsa. Além disso, tudo é como no exemplo acima - acionamos o gatilho, verificamos o resultado.
ExpectNoException - o antônimo de ExpectException
Nos casos em que definitivamente não deve ocorrer uma exceção, existe um procedimento ExpectNoException. Funciona da mesma maneira que uma ExpectException, exceto que o teste é considerado falhado se ocorrer uma exceção.
Exemplo 3: semáforo
A situação é a seguinte. Há vários procedimentos armazenados e serviços do Windows. O início de sua execução pode ser causado por vários eventos externos. Além disso, a ordem permitida de sua execução é fixa. Um semáforo é usado para diferenciar o acesso às tabelas do banco de dados. É um grupo de procedimentos armazenados.
Por exemplo, considere um desses procedimentos. Temos duas tabelas:
A tabela [Process] contém uma lista de processos permitidos para execução, [ProcStatus] - uma lista de status desses processos.
O que nosso procedimento faz? Quando chamado, uma série de verificações ocorre primeiro:
- O nome do processo a ser iniciado, transmitido no parâmetro do procedimento, é pesquisado no campo [Nome] da tabela [Processo].
- Se o nome do processo foi encontrado, é verificado se atualmente é possível iniciá-lo - o sinalizador [IsRunable] da tabela [Process].
- Se o processo for aceitável para execução, resta garantir que ele ainda não esteja em execução. Na tabela [ProcStatus], a ausência de registros sobre esse processo com o status = 'InProg' é verificada.
Se tudo estiver correto, um novo registro sobre esse processo com o status 'InProg' será adicionado ao ProcStatus (isso é considerado um lançamento), o ID desse registro será retornado com o parâmetro ProcStatusId. Se alguma verificação falhar, esperamos o seguinte:
- Uma carta é enviada ao administrador do sistema.
- Retorna ProcStatusId = -1.
- Uma nova entrada em [ProcStatus] não é adicionada.
Vamos escrever um caso de teste para testar o caso quando o processo não estiver na lista dos aceitáveis.
Por conveniência, aplique imediatamente o FakeTable. Aqui não é tão fundamentalmente importante, mas pode ser útil:
- Temos a garantia de nos livrar de quaisquer dados que possam interferir na execução correta do caso de teste.
- Simplificaremos a verificação adicional de entradas ausentes no ProcStatus.
EXEC tSQLt.FakeTable 'dbo.Process'; EXEC tSQLt.FakeTable 'dbo.ProcStatus';
Para enviar uma mensagem, é usado o procedimento [SendEmail] escrito por nossos programadores. Para verificar o envio de uma carta aos administradores, precisamos atender a ligação dela. Nesse caso, o tSQLt nos oferece o uso do SpyProcedure.
EXEC tSQLt.SpyProcedure 'dbo.SendEmail'
O SpyProcedure faz o seguinte:
- Cria uma tabela no formato [dbo]. [SendEmail_SpyProcedureLog].
- Como o FakeTable, ele substitui o procedimento original pelo seu próprio, com o mesmo nome, mas contendo a lógica de log. Se desejar, você pode adicionar qualquer uma de sua própria lógica.
Como você pode imaginar, o registro ocorre na tabela [dbo]. [SendEmail_SpyProcedureLog]. Esta tabela contém a coluna [_ID_] - o número de sequência da chamada de procedimento. As colunas subseqüentes exibem os nomes dos parâmetros passados para o procedimento e os valores passados nas chamadas são coletados neles. Se necessário, eles também podem ser verificados.
A última coisa que precisamos fazer antes de chamar o semáforo e a verificação é criar uma variável na qual colocaremos o ID do registro da tabela [ProcStatus] (mais precisamente -1, porque o registro não será adicionado).
DECLARE @ProcStatusId BIGINT;
Ligue para o semáforo:
EXEC dbo.[Semaphore_JobStarter] 'SomeProcess', @ProcStatusId OUTPUT;
É isso, agora temos todos os dados necessários para verificação. Vamos começar verificando a remessa.
letras:
IF NOT EXISTS ( SELECT * FROM dbo.SendEmail_SpyProcedureLog) EXEC tSQLt.Fail 'SendEmail has not been run.';
Nesse caso, decidimos não verificar os parâmetros transmitidos durante o envio, mas simplesmente verificar o fato em si. Chamo sua atenção para o procedimento tSQLt.Fail. Ele permite que você "oficialmente" falhe no caso de teste. Se você precisar criar alguma construção específica, o tSQLt.Fail permitirá que você faça isso.
Em seguida, verifique a ausência de entradas no [dbo]. [ProcStatus]:
EXEC tSQLt.AssertEmptyTable 'dbo.ProcStatus';
É aqui que a FakeTable que aplicamos desde o início nos ajudou. Graças a ele, podemos esperar um vazio. Sem ele, para uma verificação precisa, devemos, em boa medida, comparar o número de registros antes e depois do semáforo.
Igualdade ProcStatusId = -1, podemos verificar facilmente com AssertEquals:
EXEC tSQLt.AssertEquals -1, @ProcStatusId, 'Wrong ProcStatusId.';
AssertEquals é minimalista - simplesmente compara dois valores, nada sobrenatural.
Procedimentos de Irmãos AssertEquals
Para comparar os valores, somos fornecidos com vários procedimentos:
- AssertEquals
- AssertNotEquals
- AssertEqualsString
- Assertike
Eu acho que os nomes deles falam por si. A única coisa que vale a pena notar é a existência de um procedimento AssertEqualsString separado. O problema é que AssertEquals / AssertNotEquals / AssertLike trabalha com SQL_VARIANT e o NVARCHAR (MAX) não se aplica a ele, e, portanto, os desenvolvedores do tSQLt tiveram que alocar um procedimento separado para testar o NVARCHAR (MAX).
Função falsa
O FakeFunction com algum alongamento pode ser chamado de procedimento semelhante ao SpyProcedure. Esse falso permite que você substitua qualquer função pela mais simples necessária. Como as funções no SQL Server funcionam com o princípio de um tubo com pasta de dente - elas fornecem o resultado através do "único furo tecnológico", então, infelizmente, nenhuma funcionalidade de log é fornecida. Apenas um substituto para a lógica.
Armadilhas
Vale a pena notar algumas armadilhas que você pode encontrar ao trabalhar com o tSQLt. Nesse caso, por armadilhas, quero dizer alguns problemas problemáticos que nasceram devido às limitações do SQL Server e / ou que não podem ser resolvidos pelos desenvolvedores da estrutura.
Cancelamento / corrupção de transações
O primeiro e mais importante problema que nossa equipe enfrentou foi o cancelamento de transações. O SQL Server não sabe como reverter transações aninhadas separadamente - apenas tudo como um todo, até as mais externas. Dado o fato de o tSQLt agrupar cada caso de teste em uma transação separada, isso se torna um problema. Afinal, uma reversão de transação dentro do procedimento de teste pode interromper a execução do teste, causando um erro de execução não descritivo.
Para contornar esse problema, usamos pontos de salvamento. A ideia é simples. Antes de iniciar uma transação no procedimento de teste, verificamos se já estamos dentro da transação. Se acontecer que sim, estamos assumindo que essa é uma transação tSQLt, coloque o savepoint em vez de iniciar. Então, se necessário, reverteremos para esse ponto de salvamento, e não para o início da transação. Aninhamento como tal não é.
O problema é complicado pela corrupção da transação. Se de repente algo deu errado e a exceção funcionou, a transação pode se tornar uma condenação. Essa transação pode não apenas ser confirmada, mas também revertida para o savepoint, revertida apenas a coisa toda.
Dado todo o exposto, você deve aplicar o seguinte design:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END; BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Considere o código em partes. Primeiro, precisamos determinar se estamos dentro de uma transação:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END;
Após receber o sinalizador @isNestedTransaction, execute o bloco TRY e defina o savepoint ou o início da transação, dependendo da situação.
BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Depois de termos feito algo útil, confirme se este é um início "real" do procedimento.
Obviamente, se este é um lançamento de um caso de teste, não precisamos confirmar nada. No final da execução, o tSQLt simplesmente reverterá todas as alterações. Se de repente algo deu errado e entramos no bloco CATCH, a primeira coisa a fazer é descobrir se nossa transação pode até ser confirmada.
BEGIN CATCH DECLARE @isCommitable BIT = CASE WHEN XACT_STATE() = 1 THEN 'true' ELSE 'false' END;
Só podemos reverter para o savepoint se
- transação comprometível
- ocorre uma execução de teste, ou seja, existe um ponto de salvamento.
Em outros casos, precisamos reverter toda a transação.
IF @isCommitable = 'true' AND @isNestedTransaction = 'true' ROLLBACK TRANSACTION SavepointName; ELSE ROLLBACK; THROW; END CATCH;
Sim, infelizmente, se durante uma execução de teste obtivemos uma transação inconfundível, ainda recebemos um erro na execução do caso de teste.
FakeTable e problema com chave estrangeira
Considere as tabelas familiares [Trial] e [Clinic]:
Lembramos do [TrialID] FK. Que problemas isso pode causar? Nos exemplos dados anteriormente, aplicamos o FakeTable nas duas tabelas ao mesmo tempo. Se aplicarmos apenas em [Trial], obteremos a seguinte situação:
Uma tentativa de inserir uma entrada em [Clinic], dessa maneira, pode resultar em falha (mesmo que tenhamos preparado todos os dados necessários na versão falsa da tabela [Trial]).
[dbo].[Test_FK_Problem] failed: (Error) The INSERT statement conflicted with the FOREIGN KEY constraint "Trial_Fk". The conflict occurred in database "HabrDemo", table "dbo.tSQLt_tempobject_ba8f36353f7a44f6a9176a7d1db02493", column 'TrialID'.[16,0]{Test_FK_Problem,14}
Conclusão: você precisa fingir tudo ou não fingir nada. No segundo caso, é óbvio que a base deve ser preparada com antecedência para teste.
SpyProcedure nos procedimentos do sistema
Infelizmente, a espionagem de chamadas para procedimentos do sistema falhará.
[HabrDemo].[test_test] failed: (Error) Cannot use SpyProcedure on sys.sp_help because the procedure does not exist[16,10] {tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure,7}
No exemplo do semáforo, rastreamos as chamadas para o procedimento [SendEmail] escrito por nossos desenvolvedores. Nesse caso, a gravação de um procedimento separado deve-se à necessidade de coletar e processar algumas informações adicionais antes de enviar as mensagens diretamente. Em geral, é preciso estar mentalmente preparado para o fato de que talvez seja necessário escrever procedimentos entre camadas para alguns procedimentos do sistema apenas para fins de teste.
Vantagens
Instalação rápida
A instalação ocorre em 2 etapas e leva cerca de 2 minutos. Você só precisa ativar o CLR no servidor, se ainda não estiver pronto, e executar um único script. Tudo, você pode adicionar a primeira classe de teste e escrever casos de teste.
Desenvolvimento rápido
O tSQLt é uma ferramenta fácil de aprender. Levei um pequeno dia para dominá-lo. Perguntei aos meus colegas que trabalhavam com a estrutura, e acabou que todos gastariam cerca de um dia.
Implementação rápida no IC
Demorou cerca de 2 horas para configurar a integração no CI em nosso projeto. O tempo, é claro, pode variar, mas em geral isso não é um problema, e a integração pode ser realizada muito rapidamente.
Ampla gama de ferramentas
Essa é uma avaliação subjetiva, mas, na minha opinião, a funcionalidade fornecida pelo tSQLt é bastante rica e cobre a maior parte das necessidades na prática. Para casos raros, quando não há falsificações e asserções internas suficientes, existe, é claro, tSQLt.Fail.
Documentação conveniente
A documentação oficial é conveniente e consistente. Com sua ajuda, você pode entender facilmente a essência do uso do tSQLt em um curto espaço de tempo, mesmo que essa seja sua primeira ferramenta de teste de unidade.
Saída conveniente
Os dados podem ser obtidos em uma forma de texto muito claro:
[tSQLtDemo].[test_error_messages] failed: (Failure) Expected an error to be raised. [tSQLtDemo].[test_tables_comparison] failed: (Failure) useful and descriptive error message Unexpected/missing resultset rows! |_m_|Column1|Column2| +---+-------+-------+ |< |2 |Value2 | |= |1 |Value1 | |= |3 |Value3 | |> |2 |Value3 | +----------------------+ |Test Execution Summary| +----------------------+ |No|Test Case Name |Dur(ms)|Result | +--+------------------------------------+-------+-------+ |1 |[tSQLtDemo].[test_constraint] | 83|Success| |2 |[tSQLtDemo].[test_trial_view] | 83|Success| |3 |[tSQLtDemo].[test_error_messages] | 127|Failure| |4 |[tSQLtDemo].[test_tables_comparison]| 147|Failure| ----------------------------------------------------------------------------- Msg 50000, Level 16, State 10, Line 1 Test Case Summary: 4 test case(s) executed, 2 succeeded, 2 failed, 0 errored. -----------------------------------------------------------------------------
Você também pode extrair do banco de dados (clicável) ...
... ou entre no formato XML.
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite id="1" name="tSQLtDemo" tests="3" errors="0" failures="1" timestamp="2019-06-22T16:46:06" time="0.433" hostname="BLAHBLAHBLAH\SQL2017" package="tSQLt"> <properties /> <testcase classname="tSQLtDemo" name="test_constraint" time="0.097" /> <testcase classname="tSQLtDemo" name="test_error_messages" time="0.153"> <failure message="Expected an error to be raised." type="tSQLt.Fail" /> </testcase> <testcase classname="tSQLtDemo" name="test_trial_view" time="0.156" /> <system-out /> <system-err /> </testsuite> </testsuites>
A última opção permite integrar facilmente os testes ao IC. Em particular, tudo funciona para nós no Atlassian Bamboo.
Suporte Redgate
As vantagens incluem suporte para um grande fornecedor de ferramentas DBA como o RedGate. O Teste SQL - seu plug-in para o SQL Server Management Studio - funciona com o tSQLt imediatamente. Além disso, o RedGate fornece assistência ao desenvolvedor principal do tSQLt com o ambiente de desenvolvimento, como afirma o próprio desenvolvedor dos
grupos do Google .
Desvantagens
Sem falsificações temporárias na mesa
O tSQLt não permite tabelas temporárias falsas. Se necessário, você pode usar o
complemento não oficial . Infelizmente, este complemento é suportado apenas pelo SQL Server 2016+.
Sem acesso a bancos de dados externos
Não funcionará para manter uma base separada apenas para armazenar a estrutura. O tSQLt foi projetado para testar o que está no mesmo banco de dados. Falso, infelizmente, não vai funcionar.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db] AS BEGIN SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] EXEC tSQLt.FakeTable '[AdventureWorks2017].[Person].[Password]' SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] END
As afirmações parecem funcionar, mas ninguém garante seu desempenho, é claro.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db_assertions] AS BEGIN SELECT TOP 1 * INTO
Erros de documentação
Apesar de ter escrito acima sobre a consistência e acessibilidade da documentação, ela também contém problemas. Existem alguns momentos desatualizados.
Exemplo 1.
“Guia de início rápido” sugere o download da estrutura do SourceForge. Eles se despediram do SourceForge
em 2015 .
Exemplo 2. O
guia ApplyConstraint no exemplo usa um procedimento Fail para capturar uma exceção, que seria mais fácil e mais visual para substituir por ExpectException.
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK prevents insert of orphaned rows] AS BEGIN EXEC tSQLt.FakeTable 'dbo.ReferencedTable'; EXEC tSQLt.FakeTable 'dbo.ReferencingTable'; EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK'; DECLARE @ErrorMessage NVARCHAR(MAX); SET @ErrorMessage = ''; BEGIN TRY INSERT INTO dbo.ReferencingTable ( id, ReferencedTableId ) VALUES ( 1, 11 ) ; END TRY BEGIN CATCH SET @ErrorMessage = ERROR_MESSAGE(); END CATCH IF @ErrorMessage NOT LIKE '%ReferencingTable_ReferencedTable_FK%' BEGIN EXEC tSQLt.Fail 'Expected error message containing ''ReferencingTable_ReferencedTable_FK'' but got: ''',@ErrorMessage,'''!'; END END GO
E isso é natural, porque acontece ...
Abandono parcial
Há uma longa pausa no desenvolvimento do tSQLt do início de 2016 a junho de 2019. Sim, infelizmente, essa ferramenta está parcialmente abandonada. Em 2019, pouco a pouco, a
julgar pelo GitHub , o desenvolvimento ainda avançou. Embora os Grupos oficiais do Google
tenham um tópico em que Sebastian, o principal desenvolvedor do tSQLt, foi perguntado diretamente sobre o destino do desenvolvimento. A última pergunta foi feita em 2 de março de 2019, a resposta ainda não foi recebida.
Problema com o SQL Server 2017
Se você estiver usando o SQL Server 2017, para você, possivelmente a instalação do tSQLt exigirá alguma manipulação adicional. O fato é que, pela primeira vez desde 2012, o SQL Server fez alterações na segurança. No nível do servidor, o sinalizador “CLR strict security” foi adicionado, o que proíbe a criação de assemblies não assinados (mesmo o SAFE). Uma descrição detalhada do problema merece um artigo separado (e, felizmente, tudo já está bem descrito e os artigos subsequentes da série). Apenas esteja mentalmente preparado para isso.Obviamente, pode-se atribuir essa desvantagem a "armadilhas", cuja solução não depende dos desenvolvedores do tSQLt, mas é possível resolver esse problema no nível da estrutura, embora demore um pouco. O GitHub já tem um problema É verdade que, com a permissão dele, eles se arrastam desde outubro de 2017 (veja o parágrafo anterior).Alternativas (±) para outros DBMSs
Também vale a pena mencionar alternativas para outros DBMSs. O tSQLt não é a única ferramenta desse tipo. Embora, devido aos recursos de implementação (CLR e T-SQL sejam significativamente diferentes de outros dialetos SQL), você não possa usá-lo em outros DBMSs, ainda poderá encontrar ferramentas semelhantes. Observo que essas alternativas diferem significativamente do tSQLt, por isso estamos falando principalmente sobre a abordagem baseada em SQL como um todo.Portanto, no PostgreSQL, existe um ptTAP bastante desenvolvido e ativamente desenvolvido . Envolve a gravação de testes no PL / pgSQL nativo e a saída dos resultados no formato TAP. No MySQL, existe uma ferramenta semelhante, embora um pouco menos funcional - MyTAP . Se você tiver sorte de trabalhar com o Oracle, terá a oportunidade de usar o utPLSQL- Uma ferramenta muito poderosa e ativamente (eu diria, mais do que) uma ferramenta de desenvolvimento.Conclusão
Talvez, com todas as informações acima, eu quis transmitir duas idéias principais.A primeira é a utilidade de testar o código em um banco de dados. Esteja você sentado no SQL Server, Oracle ou MySQL, isso não importa. Se você possui uma certa quantidade de lógica não testada armazenada no banco de dados, assume riscos adicionais. Os erros no código do banco de dados são capazes, como no restante do código, de causar danos ao produto e, como resultado, à empresa que o fornece.A segunda idéia é escolher uma ferramenta. Se você, como eu, trabalha com o SQL Server, o tSQLt é, se não 100% um vencedor, definitivamente vale a pena sua atenção. Mesmo apesar do lento desenvolvimento recente, ainda é uma ferramenta relevante que facilita muito os testes.Fontes que me ajudaram (lista incompleta)