Eu sugeriria zombar de suas chamadas para o banco de dados. As zombarias são basicamente objetos que se parecem com o objeto no qual você está tentando chamar um método, no sentido de que eles têm as mesmas propriedades, métodos etc. disponíveis para o chamador. Mas, em vez de executar qualquer ação que eles estão programados para executar quando um método específico é chamado, ele ignora isso e retorna um resultado. Esse resultado geralmente é definido por você com antecedência.
Para configurar seus objetos para zombaria, você provavelmente precisará usar algum tipo de inversão do padrão de injeção de controle / dependência, como no pseudocódigo a seguir:
class Bar
{
private FooDataProvider _dataProvider;
public instantiate(FooDataProvider dataProvider) {
_dataProvider = dataProvider;
}
public getAllFoos() {
// instead of calling Foo.GetAll() here, we are introducing an extra layer of abstraction
return _dataProvider.GetAllFoos();
}
}
class FooDataProvider
{
public Foo[] GetAllFoos() {
return Foo.GetAll();
}
}
Agora, no seu teste de unidade, você cria uma simulação do FooDataProvider, que permite chamar o método GetAllFoos sem precisar acessar o banco de dados.
class BarTests
{
public TestGetAllFoos() {
// here we set up our mock FooDataProvider
mockRepository = MockingFramework.new()
mockFooDataProvider = mockRepository.CreateMockOfType(FooDataProvider);
// create a new array of Foo objects
testFooArray = new Foo[] {Foo.new(), Foo.new(), Foo.new()}
// the next statement will cause testFooArray to be returned every time we call FooDAtaProvider.GetAllFoos,
// instead of calling to the database and returning whatever is in there
// ExpectCallTo and Returns are methods provided by our imaginary mocking framework
ExpectCallTo(mockFooDataProvider.GetAllFoos).Returns(testFooArray)
// now begins our actual unit test
testBar = new Bar(mockFooDataProvider)
baz = testBar.GetAllFoos()
// baz should now equal the testFooArray object we created earlier
Assert.AreEqual(3, baz.length)
}
}
Um cenário de zombaria comum, em poucas palavras. É claro que você provavelmente ainda desejará testar também suas chamadas de banco de dados reais, pelas quais precisará acessar o banco de dados.
Idealmente, seus objetos devem ser persistentes e ignorantes. Por exemplo, você deve ter uma "camada de acesso a dados", para a qual solicitaria e que retornaria objetos. Dessa forma, você pode deixar essa parte fora dos testes de unidade ou testá-los isoladamente.
Se seus objetos estão fortemente acoplados à sua camada de dados, é difícil fazer o teste de unidade adequado. a primeira parte do teste de unidade é "unidade". Todas as unidades devem poder ser testadas isoladamente.
Nos meus projetos c #, eu uso o NHibernate com uma camada de dados completamente separada. Meus objetos vivem no modelo de domínio principal e são acessados na minha camada de aplicativo. A camada de aplicativo conversa com a camada de dados e a camada de modelo de domínio.
A camada de aplicação também é chamada de "Camada de negócios".
Se você estiver usando PHP, crie um conjunto específico de classes para ONLY acesso a dados. Verifique se seus objetos não têm idéia de como eles são persistentes e conecte os dois nas classes de aplicativos.
Outra opção seria usar zombarias / stubs.
fonte
A maneira mais fácil de testar um objeto com acesso ao banco de dados é usando escopos de transação.
Por exemplo:
Isso reverterá o estado do banco de dados, basicamente como uma reversão de transação, para que você possa executar o teste quantas vezes quiser, sem efeitos colaterais. Usamos essa abordagem com sucesso em grandes projetos. Nossa compilação demora um pouco para ser executada (15 minutos), mas não é horrível ter 1800 testes de unidade. Além disso, se o tempo de compilação for uma preocupação, você pode alterar o processo de compilação para ter várias compilações, uma para a criação de src, outra que é acionada posteriormente que lida com testes de unidade, análise de código, empacotamento, etc.
fonte
Talvez eu possa lhe dar um gostinho da nossa experiência quando começamos a analisar os testes de unidade de nosso processo de camada intermediária, que incluíam uma tonelada de operações sql de "lógica de negócios".
Primeiro, criamos uma camada de abstração que nos permitia "encaixar" qualquer conexão de banco de dados razoável (no nosso caso, simplesmente suportávamos uma única conexão do tipo ODBC).
Quando isso aconteceu, pudemos fazer algo assim em nosso código (trabalhamos em C ++, mas tenho certeza de que você entendeu):
GetDatabase (). ExecuteSQL ("INSERIR EM FOO (blá, blá)")
Em tempo de execução normal, GetDatabase () retornaria um objeto que alimentava todo o nosso sql (incluindo consultas), via ODBC diretamente no banco de dados.
Começamos então a olhar para os bancos de dados na memória - o melhor parece ser o SQLite. ( http://www.sqlite.org/index.html ). É incrivelmente simples de configurar e usar, e nos permitiu subclassar e substituir GetDatabase () para encaminhar o sql para um banco de dados na memória que foi criado e destruído para cada teste realizado.
Ainda estamos nos estágios iniciais disso, mas parece bom até agora, no entanto, precisamos garantir a criação de quaisquer tabelas necessárias e preenchê-las com dados de teste - no entanto, reduzimos a carga de trabalho aqui criando um conjunto genérico de funções auxiliares que pode fazer muito disso tudo por nós.
No geral, ajudou imensamente em nosso processo TDD, pois fazer o que parece ser mudanças bastante inócuas para corrigir certos bugs pode ter efeitos bastante estranhos em outras áreas (difíceis de detectar) do seu sistema - devido à natureza do sql / bancos de dados.
Obviamente, nossas experiências se concentraram em um ambiente de desenvolvimento em C ++, no entanto, tenho certeza de que talvez você possa obter algo semelhante trabalhando em PHP / Python.
Espero que isto ajude.
fonte
Você deve simular o acesso ao banco de dados se desejar testar suas classes. Afinal, você não deseja testar o banco de dados em um teste de unidade. Isso seria um teste de integração.
Abstraia as chamadas ausentes e insira uma simulação que retorne os dados esperados. Se suas classes não fazem mais do que executar consultas, pode até não valer a pena testá-las, embora ...
fonte
O livro xUnit Test Patterns descreve algumas maneiras de lidar com o código de teste de unidade que atinge um banco de dados. Eu concordo com as outras pessoas que estão dizendo que você não quer fazer isso porque é lento, mas você precisa fazê-lo em algum momento, IMO. Zombar da conexão db para testar coisas de nível superior é uma boa idéia, mas confira este livro para obter sugestões sobre coisas que você pode fazer para interagir com o banco de dados real.
fonte
Opções que você tem:
Injete o banco de dados. (Exemplo em pseudo-Java, mas se aplica a todas as linguagens OO)
agora em produção, você usa o banco de dados normal e, para todos os testes, apenas injeta o banco de dados falso que pode criar ad hoc.User
vez de uma tupla{name: "marcin", password: "blah"}
), escreva todos os seus testes com objetos reais construídos ad hoc e escreva um grande teste que depende de um banco de dados que garanta essa conversão funciona bem.É claro que essas abordagens não são mutuamente exclusivas e você pode combiná-las e combiná-las conforme necessário.
fonte
O teste unitário do acesso ao banco de dados é bastante fácil se o seu projeto tiver alta coesão e acoplamento solto por toda parte. Dessa forma, você pode testar apenas o que cada classe em particular faz sem ter que testar tudo de uma vez.
Por exemplo, se você testar a unidade da classe de interface do usuário, os testes que você escrever deverão tentar apenas verificar se a lógica dentro da interface do usuário funcionou conforme o esperado, não a lógica comercial ou a ação do banco de dados por trás dessa função.
Se você deseja testar o acesso real ao banco de dados, você acabará realizando mais um teste de integração, porque dependerá da pilha de rede e do servidor de banco de dados, mas poderá verificar se seu código SQL faz o que você solicitou. Faz.
O poder oculto do teste de unidade para mim é que ele me obriga a projetar meus aplicativos de uma maneira muito melhor do que eu faria sem eles. Isso é porque realmente me ajudou a romper com a mentalidade "essa função deve fazer tudo".
Desculpe, não tenho exemplos de código específicos para PHP / Python, mas se você quiser ver um exemplo do .NET, tenho um post que descreve uma técnica que eu costumava fazer no mesmo teste.
fonte
Normalmente, tento interromper meus testes entre testar os objetos (e ORM, se houver) e testar o banco de dados. Testo o lado do objeto, zombando das chamadas de acesso a dados, enquanto testo o lado do banco de dados, testando as interações do objeto com o banco de dados que, na minha experiência, geralmente é bastante limitado.
Eu costumava ficar frustrado com a gravação de testes de unidade até começar a zombar da parte de acesso a dados, para não precisar criar um banco de dados de teste ou gerar dados de teste em tempo real. Ao ridicularizar os dados, você pode gerar tudo em tempo de execução e garantir que seus objetos funcionem corretamente com entradas conhecidas.
fonte
Eu nunca fiz isso em PHP e nunca usei Python, mas o que você quer fazer é zombar das chamadas para o banco de dados. Para fazer isso, você pode implementar alguma IoC, seja uma ferramenta de terceiros ou você mesmo a gerencia, então você pode implementar uma versão simulada do chamador do banco de dados, onde é onde você controlará o resultado dessa chamada falsa.
Uma forma simples de IoC pode ser executada apenas codificando para Interfaces. Isso requer algum tipo de orientação a objetos em seu código, para que não se aplique ao que você está fazendo (eu digo que, como tudo o que tenho a fazer é sua menção a PHP e Python)
Espero que seja útil, se nada mais, você tem alguns termos para pesquisar agora.
fonte
Concordo com o primeiro acesso pós-banco de dados deve ser retirado em uma camada DAO que implementa uma interface. Em seguida, você pode testar sua lógica em relação à implementação de stub da camada DAO.
fonte
Você pode usar estruturas de simulação para abstrair o mecanismo de banco de dados. Eu não sei se PHP / Python tem algum, mas para linguagens digitadas (C #, Java etc.), existem muitas opções
Também depende de como você projetou o código de acesso ao banco de dados, porque alguns projetos são mais fáceis de realizar testes de unidade do que outros, como os posts anteriores mencionaram.
fonte
Configurar dados de teste para testes de unidade pode ser um desafio.
Quando se trata de Java, se você usa APIs do Spring para teste de unidade, pode controlar as transações no nível da unidade. Em outras palavras, você pode executar testes de unidade que envolvem atualizações / inserções / exclusões de banco de dados e reverter as alterações. No final da execução, você deixa tudo no banco de dados como estava antes de iniciar a execução. Para mim, é o melhor possível.
fonte