Testes unitários que precisam ler arquivos

104 views
Skip to first unread message

Guilherme Gall

unread,
Jan 5, 2012, 11:49:13 PM1/5/12
to python...@googlegroups.com
Prezados,

Iniciei um pequeno projeto pessoal recentemente e resolvi desde o
início cobrir o código com testes unitários. Estou fazendo um gerador
de sites estáticos, como o jekyll [1], só que bem mais simples.

A primeira classe que criei e desejo testar é a classe Page, que é
inicializada assim:

>>> Page('/path/para/arquivo_exemplo')

Se tudo ocorrer bem a instância de Page terá um atributo que é um
dicionário com metainformações definido no cabeçalho de
arquivo_exemplo e outro com o conteúdo em si.

Diversas situações podem ocorrer com arquivo_exemplo:

- as metainformações podem não ter sido informadas;
- as metainformações podem ter sido informadas em um formato incorreto;
- o conteúdo pode não ter sido informado;
- várias outras situações que provavelmente só perceberei ao longo do
desenvolvimento.

Gostaria de testar cada uma dessas situações, mas isso gera o problema
de eu precisar criar uma versão de arquivo_exemplo para cada situação.
Isso polui bastante o diretório no qual escrevo os arquivos e me
obriga a recriá-los caso eu vá executar os testes num computador
diferente.

Como estou usando unittest, pensei em criar os arquivos via setUp() em
/tmp e depois apagá-los com tearDown(). Melhora um pouco, mas ainda
preciso assumir uma estrutura de diretórios e permissões específica.
Num Windows, por exemplo, os testes falhariam pela ausência do /tmp,
não necessariamente por algum problema no código. A sensação que eu
tenho é que estou "acoplado" demais ao filesystem.

Qual técnica vocês utilizam quando precisam testar componentes que
dependem de leitura de arquivos com o unittest?

Desde já agradeço a atenção,

[1] http://jekyllrb.com/

Guilherme Magalhães Gall (GMGall)
GPG Public Key ID: 0F498058

Luiz Fernando

unread,
Jan 6, 2012, 12:43:15 AM1/6/12
to python...@googlegroups.com
Eu sou novato no assunto testes, mas vou tentar ajudar.

Se eu falar alguma bobagem favor me corrijam.

2012/1/6 Guilherme Gall <gmg...@gmail.com>
[cut]


A primeira classe que criei e desejo testar é a classe Page, que é
inicializada assim:

>>> Page('/path/para/arquivo_exemplo')

[cut]


Como estou usando unittest, pensei em criar os arquivos via setUp() em
/tmp e depois apagá-los com tearDown(). Melhora um pouco, mas ainda
preciso assumir uma estrutura de diretórios e permissões específica.
Num Windows, por exemplo, os testes falhariam pela ausência do /tmp,
não necessariamente por algum problema no código. A sensação que eu
tenho é que estou "acoplado" demais ao filesystem.

Tem uma definição do Michael Feathers [1] de que "A test is not a unit test if:

- It talks to the database
- It communicates across the network
- *It touches the file system*
- It can't run at the same time as any of your other unit tests
- You have to do special things to your environment (such as editing config files) to run it."


Qual técnica vocês utilizam quando precisam testar componentes que
dependem de leitura de arquivos com o unittest?


Não me parece que o que você quer testar dependa da leitura de arquivo. Depende sim do *conteúdo* do arquivo.
 
Eu faria um método que recebe o conteúdo do arquivo -- ou um objeto do tipo "file" dependendo do caso -- e devolveria os dicionários com os metadados e o conteúdo, que é o que lhe interessa. 

No __init__ da classe eu abriria o arquivo e depois chamaria esse método, daí no teste eu testaria esse método sem precisar abrir nenhum arquivo, passando apenas o conteúdo que ele precisa como parâmetro. No caso de optar usar o objeto "file" como parâmetro, criaria um mock dele para usar no teste.

Caso você tenha mesmo que criar arquivos temporários, use o módulo tempfile [2]
 

--
Luiz Armesto

Pedro Werneck

unread,
Jan 6, 2012, 7:44:42 AM1/6/12
to python...@googlegroups.com
2012/1/6 Guilherme Gall <gmg...@gmail.com>:

>
> Qual técnica vocês utilizam quando precisam testar componentes que
> dependem de leitura de arquivos com o unittest?
>

Use o módulo StringIO.

---
Pedro Werneck

Dirley

unread,
Jan 6, 2012, 11:31:54 AM1/6/12
to python...@googlegroups.com
Eu particularmente não gosto de lidar com o filesystem dentro do "meu" código. Deixo a parte de "lidar com arquivos" para o usuário da minha classe/lib/módulo sempre que possível, recebendo objetos filelike e, no máximo, verificando suas propriedades name e mode. Assim fica mais fácil de testar o código, usando um StringIO, por exemplo.

Agora, caso você queira mesmo lidar com os arquivos, uma boa saída seria escrever classes que recebem objetos filelike e funções que recebem caminhos, lidam com o filesystem e retornam instâncias da tal classe. Algo assim:

no core:
    class Page(content): # representa uma página. content deve ser um filelike que suporte leitura e seek.

num módulo "utils" ou "shortcuts" ou algo parecido
    def page(path): #abre o arquivo `path` em modo de leitura, e retorna um objeto `Page`.


Assim você poderá testar seu código com facilidade, oferecerá uma interface "baixo nível" mais flexível/extensível aos usuários do seu código e ao mesmo tempo uma interface mais "alto nível" para os casos gerais.

- D



--
------------------------------------
Grupo Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar

<*> Para visitar o site do grupo na web, acesse:
   http://groups.google.com/group/python-brasil

<*> Para sair deste grupo, envie um e-mail para:
   python-brasi...@googlegroups.com

Henrique Bastos

unread,
Jan 6, 2012, 5:39:48 PM1/6/12
to python...@googlegroups.com
Depois dê uma olhada no Hyde, projeto em Python com idéia similar.

Abs,
--
Henrique Bastos

Aprenda Python e Django na Prática!

+55 21 9618-6180



2012/1/6 Dirley <dirl...@gmail.com>

Guilherme Gall

unread,
Jan 9, 2012, 11:51:17 AM1/9/12
to python...@googlegroups.com
Prezados,

Desculpe pela demora no retorno, fiquei bem ocupado nos últimos dias.

> Luiz Fernando:

De fato quero testar o conteúdo e não a leitura em si. Antes de
escrever a 1ª versão da minha classe Page fiquei em dúvida se ela
seria inicializada com uma string com o path do arquivo ou com um
objeto file-like.

Acabei decidindo por receber o path e voltei atrás depois de ler a sua
mensagem e a do Dirley.

> Pedro Werneck:

Não esqueci do módulo StringIO, mas estava tendo dificuldades em
usá-lo na minha classe, cujas instâncias recebiam um path em __init__
ao invés de um objeto file-like. Após a refatoração, passei a usar um
StringIO no lugar de arquivos reais nos testes.

Uma coisa bem óbvia que não atentei quando escrevi minha mensagem foi
que eu poderia manter a implementação da classe Page como estava e nos
testes fazer o seguinte (supondo que x seja uma instância de Page):

>>> x.file = StringIO.StringIO()

> Dirley:

Como comentei, refatorei o minha classe de forma a receber um
file-like, como você indicou. De fato ficou mais fácil de testar.

E gostei da dica do "utils".

> Henrique Bastos:

Já conhecia o projeto Hyde e acho ele bem bacana. Mas minha intenção
era implementar alguma coisa por minha conta para aplicar alguns
conceitos. Uso de testes é um deles. Eu particularmente tenho
dificuldades ainda em implementar os testes antes das funcionalidades
em si e toda vez que leio sobre TDD se fala de escrever os testes
primeiro.

--

Muito obrigado pela atenção de todos,

Guilherme Magalhães Gall (GMGall)
GPG Public Key ID: 0F498058

Reply all
Reply to author
Forward
0 new messages