Enums são simplesmente tipos finitos, com nomes personalizados (espero que sejam significativos). Um enum pode ter apenas um valor, como o voidque contém apenas null(alguns idiomas chamam isso unite usam o nome voidde um enum sem elementos!). Pode ter dois valores, como boolqual tem falsee true. Pode ter três, como colourChannelcom red, greene blue. E assim por diante.
Se duas enumerações têm o mesmo número de valores, elas são "isomórficas"; ou seja, se mudarmos todos os nomes sistematicamente, podemos usar um no lugar do outro e nosso programa não se comportará de maneira diferente. Em particular, nossos testes não se comportarão de maneira diferente!
Por exemplo, resultcontendo win/ lose/ drawé isomorfo ao acima colourChannel, uma vez que pode substituir por exemplo, colourChannelcom result, redcom win, greencom losee bluecom draw, e enquanto fazemos isso em todos os lugares (produtores e consumidores, analisadores e serializadores, entradas de banco de dados, arquivos de log, etc. ), não haverá alterações em nosso programa. Qualquer " colourChannelteste" que escrevemos ainda será aprovado, mesmo que não exista colourChannelmais!
Além disso, se uma enumeração contiver mais de um valor, sempre podemos reorganizá- los para obter uma nova enumeração com o mesmo número de valores. Como o número de valores não mudou, o novo arranjo é isomórfico ao antigo, e, portanto, poderíamos mudar todos os nomes e nossos testes ainda passariam (observe que não podemos simplesmente mudar a definição; devemos ainda mudar todos os sites de uso também).
O que isso significa é que, no que diz respeito à máquina, enumerações são "nomes distinguíveis" e nada mais . A única coisa que podemos fazer com uma enumeração é ramificar se dois valores são iguais (por exemplo, red/ red) ou diferentes (por exemplo, red/ blue). Portanto, essa é a única coisa que um 'teste de unidade' pode fazer, por exemplo
( red == red ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
( red != green) || throw TestFailure;
( red != blue ) || throw TestFailure;
...
Como @ jesm00 diz, esse teste está verificando a implementação da linguagem, e não o seu programa. Esses testes nunca são uma boa idéia: mesmo que você não confie na implementação do idioma, você deve testá-lo de fora , pois não é possível executar os testes corretamente!
Então essa é a teoria; e a prática? O principal problema com essa caracterização das enumerações é que os programas do mundo real raramente são independentes: temos versões herdadas, implantações remotas / incorporadas, dados históricos, backups, bancos de dados ativos etc. para que nunca possamos realmente 'mudar' todas as ocorrências de um nome sem perder alguns usos.
No entanto, essas coisas não são de responsabilidade do próprio enum: alterar um enum pode interromper a comunicação com um sistema remoto, mas, inversamente, podemos resolver esse problema alterando um enum!
Em tais cenários, a enumeração é uma pista falsa: que se um sistema necessita que seja esse caminho, e outro precisa que ele seja de que maneira? Não pode ser os dois, não importa quantos testes escrevamos! O verdadeiro culpado aqui é a interface de entrada / saída, que deve produzir / consumir formatos bem definidos, em vez de "qualquer número inteiro escolhido pela interpretação". Portanto, a solução real é testar as interfaces de E / S : com testes de unidade para verificar se está analisando / imprimindo o formato esperado e com testes de integração para verificar se o formato é realmente aceito pelo outro lado.
Ainda podemos nos perguntar se o enum está sendo "exercitado completamente o suficiente", mas nesse caso o enum é novamente um arenque vermelho. Na verdade, estamos preocupados com o próprio conjunto de testes . Podemos ganhar confiança aqui de duas maneiras:
- A cobertura do código pode nos dizer se a variedade de valores de enumeração provenientes do conjunto de testes é suficiente para acionar as várias ramificações no código. Caso contrário, podemos adicionar testes que acionam as ramificações descobertas ou geram uma variedade maior de enumerações nos testes existentes.
- A verificação de propriedade pode nos dizer se a variedade de ramificações no código é suficiente para lidar com as possibilidades de tempo de execução. Por exemplo, se o código manipular apenas
rede testamos apenas red, teremos 100% de cobertura. Um verificador de propriedades (tentará) gerar contra-exemplos para nossas afirmações, como gerar os valores greene blueque esquecemos de testar.
- O teste de mutação pode nos dizer se nossas afirmações realmente verificam o enum, em vez de apenas seguir os ramos e ignorar suas diferenças.