Estou em um projeto de sistema distribuído escrito em java, onde temos algumas classes que correspondem a objetos de negócios do mundo real muito complexos. Esses objetos têm muitos métodos correspondentes às ações que o usuário (ou outro agente) pode aplicar a esses objetos. Como resultado, essas classes se tornaram muito complexas.
A abordagem da arquitetura geral do sistema levou a muitos comportamentos concentrados em poucas classes e muitos cenários possíveis de interação.
Como exemplo, e para manter as coisas fáceis e claras, digamos que Robot e Car eram classes no meu projeto.
Portanto, na classe Robot, eu teria muitos métodos no seguinte padrão:
- dormir(); isSleepAvaliable ();
- acordado(); isAwakeAvaliable ();
- caminhada (direção); isWalkAvaliable ();
- atirar (direção); isShootAvaliable ();
- turnOnAlert (); isTurnOnAlertAvailable ();
- turnOffAlert (); isTurnOffAlertAvailable ();
- recarrega(); isRechargeAvailable ();
- desligar(); isPowerOffAvailable ();
- stepInCar (Car); isStepInCarAvailable ();
- stepOutCar (carro); isStepOutCarAvailable ();
- Auto-destruição(); isSelfDestructAvailable ();
- morrer(); isDieAvailable ();
- Está vivo(); está acordado(); isAlertOn (); getBatteryLevel (); getCurrentRidingCar (); getAmmo ();
- ...
Na classe Car, seria semelhante:
- Ligar(); isTurnOnAvaliable ();
- desligar(); isTurnOffAvaliable ();
- caminhada (direção); isWalkAvaliable ();
- reabastecer(); isRefuelAvailable ();
- Auto-destruição(); isSelfDestructAvailable ();
- batida(); isCrashAvailable ();
- isOperational (); isOn (); getFuelLevel (); getCurrentPassenger ();
- ...
Cada um deles (robô e carro) é implementado como uma máquina de estado, onde algumas ações são possíveis em alguns estados e outras não. As ações alteram o estado do objeto. Os métodos de ações são lançados IllegalStateException
quando chamados em um estado inválido e os isXXXAvailable()
métodos informam se a ação é possível no momento. Embora alguns sejam facilmente dedutíveis do estado (por exemplo, no estado de sono, está disponível o estado de vigília), outros não são (para atirar, ele deve estar acordado, vivo, com munição e sem andar de carro).
Além disso, as interações entre os objetos também são complexas. Por exemplo, o carro pode conter apenas um passageiro do robô; portanto, se outro tentar entrar, uma exceção deve ser lançada; Se o carro bater, o passageiro deve morrer; Se o robô está morto dentro de um veículo, ele não pode sair, mesmo que o carro esteja ok; Se o robô estiver dentro de um carro, ele não poderá entrar em outro antes de sair; etc.
O resultado disso é que, como eu já disse, essas classes se tornaram realmente complexas. Para piorar as coisas, existem centenas de cenários possíveis quando o robô e o carro interagem. Além disso, grande parte dessa lógica precisa acessar dados remotos em outros sistemas. O resultado é que o teste de unidade tornou-se muito difícil e temos muitos problemas de teste, um causando o outro em um círculo vicioso:
- As configurações dos casos de teste são muito complexas, porque precisam criar um mundo significativamente complexo para se exercitar.
- O número de testes é enorme.
- O conjunto de testes leva algumas horas para ser executado.
- Nossa cobertura de teste é muito baixa.
- O código de teste tende a ser escrito semanas ou meses depois do código que eles testam, ou nunca.
- Muitos testes também são interrompidos, principalmente porque os requisitos do código testado foram alterados.
- Alguns cenários são tão complexos que falham no tempo limite durante a instalação (configuramos um tempo limite em cada teste, nos piores casos com 2 minutos e, mesmo assim, com o tempo limite, garantimos que não seja um loop infinito).
- Os erros entram regularmente no ambiente de produção.
Esse cenário de robô e carro é uma simplificação exagerada do que temos na realidade. Claramente, esta situação não é gerenciável. Então, estou pedindo ajuda e sugestões para: 1, reduzir a complexidade das classes; 2. Simplifique os cenários de interações entre meus objetos; 3. Reduza o tempo de teste e a quantidade de código a ser testado.
Edição:
Eu acho que não estava claro sobre as máquinas de estado. o robô é em si uma máquina de estado, com estados "adormecido", "acordado", "recarregando", "morto" etc. O carro é outra máquina de estado.
EDIT 2: Caso você esteja curioso sobre o que meu sistema realmente é, as classes que interagem são coisas como Servidor, Endereço IP, Disco, Backup, Usuário, SoftwareLicense, etc. O cenário Robô e Carro é apenas um caso que eu encontrei isso seria simples o suficiente para explicar meu problema.