Skip to content
fevereiro 11, 2010 / cassiomarques

Limpando os dados de forma confiável entre cenários do Cucumber + Selenium em bancos com chaves estrangeiras

A cada novo cenário executado nos testes com Cucumber, seu banco de dados deve ser totalmente limpo. Na verdade isso deve acontecer em qualquer tipo de teste que acesse o banco, de forma a garantir um estado conhecido para execução do teste. Normalmente os testes com Cucumber usando o Webrat já são configurados por default para trabalharem de forma transacional, ou seja, os dados são apagados a cada novo cenário de forma automática.

Cucumber::Rails::World.use_transactional_fixtures = true

Só que isso não funciona para os testes com Selenium, pois os testes neste caso são executados em dois processos distintos (um para a execução do Cucumber e outro para execução do Selenium) e não há como sincronizar os dois de forma automática, para saber quando começa o próximo cenário e então apagar o banco. Por isso, nos testes com Selenium a opção descrita acima deve ser setada como false:

Cucumber::Rails::World.use_transactional_fixtures = false

Neste caso, deve-se utilizar um bloco Before para limpar os dados do banco entre cenários:

Before do
 ModelBla.destroy_all
 ModelBle.destroy_all
 ModelBli.destroy_all
 # todos os seus outros models...
end

Feio, sujo, trabalhoso e chato. Mas o grande problema não está nisso, mas sim na possível existência de chaves estrangeiras no relacionamento entre as tabelas do banco (eu sou um grande defensor da idéia de que a integridade relacional do banco deve ser garantida também por constraints e não somente no nível da aplicação). Com o uso de chaves estrangeiras, existe uma sequência correta para que os dados sejam removidos, caso contrário o banco pode reclamar, dizendo que você está tentando deixar algum registro na base “sem pai nem mãe”.

Existem algumas soluções prontas para isso, como o DatabaseCleaner, mas ele também não trabalha legal com bases que possuam constraints para integridade relacional, vai apagando os registros de cada model em uma sequência meio que imprevísivel. Mas funciona muito bem em bases que não utilizam chaves estrangeiras.

Minha solução foi desenvolver uma técnica caseira que resolveu muito bem meu problema. Ela funciona para PostgreSQL (atualmenbte 100% dos projetos nos quais tenho trabalhado utilizam esse SGBD), mas deve existir algo semelhante na sintaxe de outros bancos de dados.

Before do
  connection = ActiveRecord::Base.connection
  connection.execute("TRUNCATE #{connection.tables.join(', ')} CASCADE")
end

Aqui estamos “baixando o nível”, no bom sentido, e utilizando sintaxe do PostgreSQL para limpar o banco de forma a ignorar todas as constraints. Através da conexão entre o ActiveRecord e o banco de dados, listamos todas as tabelas existentes e montamos uma query enviada direto à base. Aqui isso é perfeitamente seguro, afinal de contas é o ambiente para testes automatizados e não o de produção!

A técnica acima funciona pois *todas* as tabelas estão sendo removidas. O comando TRUNCATE não funciona em tabelas que possuam chaves estrangeiras a menos que todas as tabelas referenciadas sejam também truncadas. A vantagem dessa operação é que como nenhuma regra de integridade relacional é verificada, o processo é bastante rápido, sem causar nenhum tipo de impacto perceptível no desempenho dos testes.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: