Estou realmente lutando para escrever testes de unidade eficazes para um grande projeto do Django. Tenho uma cobertura de teste razoavelmente boa, mas percebi que os testes que escrevi são definitivamente testes de integração / aceitação, e não de unidade, e tenho partes críticas do meu aplicativo que não estão sendo testadas efetivamente. Eu quero corrigir isso o mais rápido possível.
Aqui está o meu problema. Meu esquema é profundamente relacional e muito orientado para o tempo, dando ao meu objeto de modelo alto acoplamento interno e muito estado. Muitos dos meus métodos de modelo consultam com base em intervalos de tempo, e eu tenho muita coisa auto_now_add
acontecendo nos campos com registro de data e hora. Portanto, use um método parecido com este, por exemplo:
def summary(self, startTime=None, endTime=None):
# ... logic to assign a proper start and end time
# if none was provided, probably using datetime.now()
objects = self.related_model_set.manager_method.filter(...)
return sum(object.key_method(startTime, endTime) for object in objects)
Como alguém se aproxima de testar algo assim?
Aqui é onde estou tão longe. Ocorre-me que o objetivo do teste de unidade deve receber um comportamento zombado by key_method
de seus argumentos, está summary
filtrando / agregando corretamente para produzir um resultado correto?
Zombar de datetime.now () é bastante direto, mas como posso zombar do resto do comportamento?
- Eu poderia usar acessórios, mas ouvi prós e contras do uso de acessórios para criar meus dados (a má manutenção é um golpe que chega em casa para mim).
- Eu também poderia configurar meus dados através do ORM, mas isso pode ser limitador, porque também tenho que criar objetos relacionados. E o ORM não permite que você mexa com os
auto_now_add
campos manualmente. - Zombar do ORM é outra opção, mas não é apenas complicado zombar de métodos ORM profundamente aninhados, mas a lógica no código ORM é zombada do teste, e a zombaria parece tornar o teste realmente dependente dos internos e dependências do função em teste.
As porcas mais difíceis de quebrar parecem ser as funções como esta, que ficam em algumas camadas de modelos e funções de nível inferior e são muito dependentes do tempo, mesmo que essas funções possam não ser super complicadas. Meu problema geral é que, por mais que pareça cortá-lo, meus testes parecem muito mais complexos do que as funções que estão testando.
fonte
Respostas:
Vou seguir em frente e registrar uma resposta para o que eu criei até agora.
Minha hipótese é que, para uma função com profundo acoplamento e estado, a realidade é que ela simplesmente precisará de muitas linhas para controlar seu contexto externo.
Aqui está a aparência do meu caso de teste, contando com a biblioteca Mock padrão:
datetime
e subverto osauto_now_add
horários para ajustar uma linha do tempo fixa do meu design. Eu pensei que o ORM não permitia isso, mas funciona bem.from datetime import datetime
para que eu possa corrigirdatetime.now()
apenas essa função (se eu zombar de toda adatetime
classe, o ORM é adequado).object.key_method()
, com funcionalidade simples, mas bem definida, que depende dos argumentos. Quero que dependa dos argumentos, porque, caso contrário, talvez eu não saiba se a lógica da função em teste está funcionando. No meu caso, ele simplesmente retorna o número de segundos entrestartTime
eendTime
. Euobject.key_method()
onew_callable
colo envolto em um lambda e o colo diretamente no uso do kwarg depatch
.summary
com argumentos diferentes para verificar a igualdade com os resultados calculados à mão esperados, respondendo pelo comportamento determinado da simulaçãokey_method
Escusado será dizer que isso é significativamente mais longo e mais complicado do que a própria função. Depende do banco de dados e não parece realmente um teste de unidade. Mas também é bastante dissociado dos elementos internos da função - apenas sua assinatura e dependências. Então eu acho que ainda pode ser um teste de unidade.
No meu aplicativo, a função é bastante essencial e sujeita a refatoração para otimizar seu desempenho. Então, acho que o problema vale a pena, apesar da complexidade. Mas ainda estou aberto a melhores idéias sobre como abordar isso. Tudo parte da minha longa jornada em direção a um estilo de desenvolvimento mais orientado a testes ...
fonte