Como chamar a atenção do programador em determinadas condições?

13

Vamos começar com um exemplo.

Digamos, eu tenho um método chamado exportque depende muito do esquema do banco de dados. E por "depende muito", quero dizer, sei que adicionar uma nova coluna a uma determinada tabela frequentemente (com muita frequência) leva à exportalteração do método correspondente (geralmente você também deve adicionar o novo campo aos dados de exportação).

Os programadores costumam esquecer de mudar de exportmétodo, pois não está claro se você deve olhar para isso. Meu objetivo é forçar o programador a tomar uma decisão explícita para determinar se ele esqueceu de observar o exportmétodo ou se não deseja adicionar um campo aos dados de exportação. E estou procurando a solução de design para esse problema.

Eu tenho duas idéias, mas as duas têm falhas.

Wrapper inteligente "Ler tudo"

Posso criar o wrapper inteligente que garante que todos os dados sejam lidos explicitamente.

Algo assim:

def export():
    checker = AllReadChecker.new(table_row)

    name    = checker.get('name')
    surname = checker.get('surname')
              checker.ignore('age') # explicitly ignore the "age" field

    result = [name, surname] # or whatever

    checker.check_now() # check all is read

    return result

Portanto, checkerafirma se table_rowcontém outros campos que não foram lidos. Mas tudo isso parece meio pesado e (talvez) afeta o desempenho.

"Verifique esse método" mais unittest

Posso apenas criar o mais unittest que se lembra do último esquema da tabela e falha sempre que a tabela é alterada. Nesse caso, o programador verá algo como "não se esqueça de verificar o exportmétodo". Para ocultar o programador de aviso (ou não - isso é um problema), faça o check-out exporte manualmente (esse é outro problema) corrija o teste adicionando novos campos a ele.

Tenho algumas outras idéias, mas elas são muito problemáticas para implementar ou difíceis de entender (e não quero que o projeto se torne um quebra-cabeça).


O problema acima é apenas um exemplo da classe mais ampla de problemas que encontro de tempos em tempos. Quero vincular alguns trechos de código e / ou infraestrutura, portanto, alterar um deles alerta imediatamente o programador para verificar outro. Normalmente, você tem algumas ferramentas simples, como extrair lógica comum ou escrever de maneira confiável, mas estou procurando a ferramenta para casos mais complexos: talvez alguns padrões de design dos quais eu conheço agora.

Vadim Pushtaev
fonte
Você pode gerar com exportbase no esquema?
Coredump
Ele não pode ser gerado de forma atômica, é por isso que devo pedir ao programador que olhe o código e tome uma decisão.
Vadim Pushtaev
Seria uma solução adicionar duas listas de nomes de campo (exportar e não exportar) à classe de exportação e ter um teste de unidade que verifique se essas duas listas juntas englobam o conjunto completo de campos do banco de dados?
Sjoerd Job Postmus
Você pode gerar automaticamente um teste que verifica se exporttem tudo o que você precisa realisticamente?
biziclop
1
um comentário no código-fonte uma solução simplista demais? Normalmente, as coisas perdem porque não há lembrete, um comentário poderia corrigir isso.
gbjbaanb

Respostas:

11

Você está no caminho certo com sua ideia de teste de unidade, mas sua implementação está errada.

Se exportestiver relacionado ao esquema e o esquema for alterado, há dois casos possíveis:

  • Ou o exportainda funciona perfeitamente bem, porque não foi afetado por uma ligeira alteração no esquema,

  • Ou quebra.

Nos dois casos, o objetivo da compilação é rastrear essa possível regressão. Vários testes - sejam testes de integração ou de sistema, funcionais ou qualquer outra coisa - garantem que seu exportprocedimento funcione com o esquema atual , independentemente do fato de ter sido alterado desde o commit anterior. Se esses testes passarem, ótimo. Se eles falharem, isso é um sinal para o desenvolvedor de que ele pode ter perdido alguma coisa e uma indicação clara de onde procurar.

Por que sua implementação está errada? Bem, por várias razões.

  1. Não tem nada a ver com testes de unidade ...

  2. ... e, na verdade, nem é um teste.

  3. A pior parte é que a correção do "teste" requer, na verdade, alterar o "teste", que está executando uma operação que não tem nenhuma relação com o export.

Em vez disso, fazendo testes reais para o exportprocedimento, verifique se o desenvolvedor corrigirá o erro export.


De maneira mais geral, quando você encontra uma situação em que uma mudança em uma classe sempre ou geralmente exige uma mudança em uma classe completamente diferente, este é um bom sinal de que você fez seu projeto errado e está violando o Princípio de Responsabilidade Única.

Enquanto falo especificamente sobre aulas, isso se aplica mais ou menos a outras entidades também. Por exemplo, uma alteração no esquema do banco de dados deve ser refletida automaticamente no seu código, por exemplo, através de geradores de código usados ​​por muitos ORMs ou pelo menos ser facilmente localizada: se eu adicionar Descriptioncoluna à Producttabela e não usar ORMs ou geradores de código, Eu pelo menos espero fazer uma única alteração dentro da Data.Productclasse do DAL, sem a necessidade de pesquisar em toda a base de código e encontrar algumas ocorrências de Productclasse na, por exemplo, camada de apresentação.

Se você não puder restringir razoavelmente a alteração a um local (porque você está em um caso em que simplesmente não funciona ou porque exige uma enorme quantidade de desenvolvimento), cria um risco de regressões . Quando eu mudo de classe A, e a classe Bem algum lugar da base de código para de funcionar, é uma regressão.

O teste reduz o risco de regressões e, o que é muito mais importante, mostra a localização de uma regressão. É por isso que, quando você sabe que alterações em um local causam problemas em uma parte completamente diferente da base de código, verifique se você tem testes suficientes que acionam alarmes assim que uma regressão aparece nesse nível.

Em todos os casos, evite confiar nesses casos apenas nos comentários. Algo como:

// If you change the following line, make sure you also change the corresponding
// `measure` value in `Scaffolding.Builder`.

nunca funciona. Não apenas os desenvolvedores não o leem na maioria dos casos, mas muitas vezes terminam removidos ou se afastam da linha em questão e se tornam impossíveis de entender.

Arseni Mourzenko
fonte
Sim, esse "teste" não é realmente um teste, é algum tipo de armadilha, algum tipo de If you change the following line...que funciona automaticamente e não pode ser simplesmente ignorado. Eu nunca vi alguém realmente usar essas armadilhas, daí a dúvida.
Vadim Pushtaev
1
O problema é que a classe Bnão para de funcionar, talvez comece a funcionar incorretamente. Mas não posso prever o futuro e não posso escrever testes que prevejam o futuro, então tento lembrar o desenvolvedor para escrever esse novo teste.
Vadim Pushtaev
@VadimPushtaev: quando se trata de teste, “talvez comece a funcionar incorretamente” e “pare de funcionar” é exatamente a mesma coisa. Você não pode prever o futuro, mas deve saber exatamente os requisitos e testar se esses requisitos são atendidos pelo seu produto real.
Arseni Mourzenko
Sim, isso é uma coisa. Na verdade, quero lembrar o desenvolvedor de pensar em novos requisitos . Eu só quero acenar com a mão: “Olá, você tem certeza de que não se esquece da exportação? Pergunte ao gerente ou algo assim, é um problema comum ”.
Vadim Pushtaev
De qualquer forma, obrigado, sua resposta me ajudou a organizar meus pensamentos e eu tenho um certo plano agora.
Vadim Pushtaev
3

Parece-me que suas alterações estão subespecificadas. Digamos que você mora em algum lugar que não tenha códigos postais e, portanto, não tenha uma coluna de código postal na tabela de endereços. Em seguida, os códigos postais são introduzidos ou você começa a lidar com clientes que moram onde há códigos postais e é necessário adicionar essa coluna à tabela.

Se o item de trabalho disser apenas "adicionar coluna de código postal à tabela de endereços", sim, a exportação será interrompida ou, pelo menos, não exportará códigos postais. Mas e a tela de entrada usada para inserir códigos postais? O relatório que lista todos os clientes e seus endereços? Há muitas coisas que precisam mudar quando você adiciona esta coluna. O trabalho de lembrar essas coisas é importante - você não deve contar com artefatos de código aleatórios para "prender" os desenvolvedores a se lembrarem.

Quando é tomada a decisão de adicionar uma coluna significativa (ou seja, não apenas uma pesquisa total ou desnormalizada em cache ou outro valor que não pertença a uma exportação ou relatório ou em uma tela de entrada), os itens de trabalho criados devem incluir TODAS as alterações necessário - adicionando a coluna, atualizando o script de preenchimento, atualizando os testes, atualizando a exportação, os relatórios, as telas de entrada e assim por diante. Nem todos podem ser atribuídos à (ou escolhidos pela) mesma pessoa, mas todos devem ser concluídos.

Às vezes, os desenvolvedores optam por adicionar as próprias colunas como parte da implementação de algumas mudanças maiores. Por exemplo, alguém pode ter escrito um item de trabalho para adicionar algo a uma tela de entrada e a um relatório, sem pensar em como ele é implementado. Se isso acontecer muito, você precisará decidir se o adicionador de item de trabalho precisa conhecer os detalhes da implementação (para poder adicionar todos os itens de trabalho corretos) ou se os desenvolvedores precisam estar cientes de que o item de trabalho- adicionador às vezes deixa as coisas de fora. Se for o último, você precisará de uma cultura de "não apenas mude o esquema; pare e pense no que mais isso afeta".

Se houvesse muitos desenvolvedores e isso acontecesse mais de uma vez, eu configuraria um alerta de check-in para que o líder da equipe ou outra pessoa sênior fosse alertada sobre alterações de esquema. Essa pessoa poderia procurar itens de trabalho relacionados para lidar com as consequências da mudança de esquema e, se os itens estivessem ausentes, não só poderia adicioná-los, mas também educar quem os deixasse de fora do plano.

Kate Gregory
fonte
2

Quase sempre, ao criar uma exportação, também crio uma importação correspondente. Como tenho outros testes que preenchem totalmente a estrutura de dados que está sendo exportada, posso criar um teste de unidade de ida e volta que compara um original totalmente preenchido com uma cópia exportada e importada. Se forem iguais, a exportação / importação estará concluída; se não forem iguais, o teste de unidade falhará e eu sei que o mecanismo de exportação precisa ser atualizado.

Pete Kirkham
fonte
isso pode verificar se todos os objetos são salvos e recarregados do db. Ele não cobre o caso de um campo db existente não ter um campo de objeto correspondente.
K3b
@ k3b verdadeiro. Pelo menos nos meus sistemas, isso geralmente ocorre se algo não é mais usado, mas não foi removido do esquema, que está fora do escopo do mecanismo de exportação - os testes de unidade quanto à persistência do objeto verificam se cada campo de um objeto é persistiu, mas não testei colunas não utilizadas, pois isso não teria efeito observável na função do sistema.
Pete Kirkham