Considere um método para embaralhar aleatoriamente elementos em uma matriz. Como você escreveria um teste de unidade simples, porém robusto, para garantir que isso funcione?
Eu vim com duas idéias, ambas com falhas visíveis:
- Embaralhe a matriz e verifique se a ordem é diferente da anterior. Isso soa bem, mas falha se o embaralhar na mesma ordem. (Improvável, mas possível.)
- Embaralhe a matriz com uma semente constante e verifique-a com a saída predeterminada. Isso depende da função aleatória sempre retornando os mesmos valores, dada a mesma semente. No entanto, isso às vezes é uma suposição inválida .
Considere uma segunda função que simula a rolagem de dados e retorna um número aleatório. Como você testaria essa função? Como você testaria se a função ...
- nunca retorna um número fora dos limites especificados?
- retorna números em uma distribuição válida? (Uniforme para um dado, normal para um grande número de dados.)
Estou procurando respostas que ofereçam informações sobre o teste não apenas desses exemplos, mas de elementos aleatórios do código em geral. Os testes de unidade são a solução certa aqui? Caso contrário, que tipo de testes são?
Só para facilitar a mente de todos, não estou escrevendo meu próprio gerador de números aleatórios.
testing
unit-testing
random
dlras2
fonte
fonte
Respostas:
Não acho que os testes de unidade sejam a ferramenta certa para testar a aleatoriedade. Um teste de unidade deve chamar um método e testar o valor retornado (ou estado do objeto) em relação a um valor esperado. O problema com o teste da aleatoriedade é que não há um valor esperado para a maioria das coisas que você deseja testar. Você pode testar com uma determinada semente, mas isso apenas testa a repetibilidade . Não oferece nenhuma maneira de medir o quão aleatória é a distribuição, ou se é mesmo aleatória.
Felizmente, existem muitos testes estatísticos que você pode executar, como a Bateria de Testes Aleatórios Diehard . Veja também:
Como testar a unidade de um gerador de números pseudo-aleatórios?
Teste de unidade com funções que retornam resultados aleatórios
A aleatoriedade do teste de unidade é um artigo do wiki que fala sobre muitos dos desafios já abordados ao tentar testar o que, por sua natureza, não é repetível. Uma parte interessante que eu recolhi foi a seguinte:
fonte
1. Teste de unidade seu algoritmo
Para a primeira pergunta, eu criaria uma classe falsa que você alimenta uma sequência de números aleatórios para a qual você conhece o resultado do seu algoritmo. Dessa forma, você garante que o algoritmo que você constrói sobre sua função aleatória funcione. Então, algo como:
2. Veja se sua função aleatória faz sentido
No teste de unidade, você deve adicionar um teste que é executado várias vezes e afirma que os resultados
2
aumento entre 10% e 20% (1/6 = 16,67%) do tempo desde que você o tenha rolado 1000 vezes).3. Teste de integração para o algoritmo e a função aleatória
Com que frequência você espera que sua matriz seja classificada na classificação original? Classifique algumas centenas de vezes e afirme que apenas x% do tempo a classificação não muda.
Na verdade, isso já é um teste de integração, você está testando o algoritmo junto com a função aleatória. Depois de usar a função aleatória real, você não poderá mais executar testes únicos.
Por experiência (escrevi um algoritmo genético), eu diria que combinar o teste de unidade do seu algoritmo, o teste de distribuição de sua função aleatória e o teste de integração é o caminho a seguir.
fonte
Um aspecto dos PRNGs que parece esquecido é que todas as suas propriedades são de natureza estatística: você não pode esperar que o embaralhamento de uma matriz resulte em uma permutação diferente daquela com a qual você começou. Basicamente, se você estiver usando um PRNG normal, a única coisa garantida é que ele não use um padrão simples (espero) e que tenha distribuição uniforme entre o conjunto de números que retorna.
Um teste adequado para um PRNG envolve executá-lo pelo menos 100 vezes e, em seguida, verificar a distribuição da saída (que é uma resposta direta à segunda parte da pergunta).
Uma resposta para a primeira pergunta é quase a mesma: execute o teste cerca de 100 vezes com {1, 2, ..., n} e conte o número de vezes que cada elemento esteve em cada posição. Eles devem ser todos aproximadamente iguais se o método aleatório for bom.
Uma questão totalmente diferente é como testar os PRNGs de nível de criptografia. Este é um assunto em que você provavelmente não deve se deter, a menos que saiba realmente o que está fazendo. Sabe-se que as pessoas destroem (leia-se: abrir brechas catastróficas) bons sistemas de criptografia com apenas algumas 'otimizações' ou edições triviais.
EDIT: Reli completamente a pergunta, a resposta principal e a minha. Embora os argumentos que afirmo ainda permaneçam, eu responderia em segundo a resposta de Bill The Lizard. Os testes de unidade são booleanos por natureza - ou falham ou são bem-sucedidos e, portanto, não são adequados para testar "quão boas" são as propriedades de um PRNG (ou um método usando um PRNG), pois qualquer resposta a essa pergunta seria quantitativa , em vez de polar.
fonte
Existem duas partes para isso: testar a randomização e testar coisas que usam a randomização.
O teste da randomização é relativamente direto. Você verifica se o período do gerador de números aleatórios é o esperado (para algumas amostras usando algumas sementes meio aleatórias, dentro de algum limite) e que a distribuição da saída em um tamanho de amostra grande é a esperada deve estar (dentro de algum limite).
Testar coisas que usam a randomização é melhor realizado com um gerador de números psuedo-aleatórios determinístico. Como a saída da randomização é conhecida com base na semente (suas entradas), é possível realizar o teste de unidade normalmente com base nas entradas versus saídas esperadas. Se o seu RNG é não determinística, então zombar-lo com um que é determinística (ou simplesmente não aleatório). Teste a randomização isoladamente do código que a consome.
fonte
Deixe-o executar várias vezes e visualize seus dados .
Aqui está um exemplo de shuffle do Coding Horror , você pode ver que o algoritmo está bom ou não:
É fácil ver que todos os itens possíveis são retornados pelo menos uma vez (os limites estão corretos) e que a distribuição está correta.
fonte
Indicadores gerais que achei úteis ao lidar com código que recebe entrada aleatória: Verifique os casos extremos da aleatoriedade esperada (valores máx. E mín. E máx. + 1 e min-1, se aplicável). Verifique os locais (ativado, acima e abaixo) onde os números têm pontos de inflexão (-1, 0, 1 ou maiores que 1, menores que 1 e não negativos para os casos em que um valor fracionário pode atrapalhar a função). Verifique alguns lugares completamente fora da entrada permitida. Verifique alguns casos típicos. Você também pode adicionar uma entrada aleatória, mas, para um teste de unidade que tenha o efeito colateral indesejável de que o mesmo valor não esteja sendo testado toda vez que o teste for executado (uma abordagem de semente pode funcionar, teste os primeiros 1.000 números aleatórios da semente) S ou algo assim).
Para testar a saída de uma função aleatória, é importante identificar o objetivo. No caso de cartões, o objetivo é testar a uniformidade do gerador aleatório 0-1, para determinar se todos os 52 cartões aparecem no resultado ou algum outro objetivo (talvez toda essa lista e mais)?
No exemplo específico, você deve assumir que seu gerador de números aleatórios é opaco (da mesma forma que não faz sentido testar a unidade syscall ou malloc do sistema operacional - a menos que você escreva sistemas operacionais). Pode ser útil medir o gerador de números aleatórios, mas seu objetivo não é escrever um gerador aleatório, apenas para ver que você recebe 52 cartas de cada vez e que elas mudam de ordem.
É um longo caminho para dizer que existem realmente duas tarefas de teste aqui: testar se o RNG está produzindo a distribuição correta e verificar se o código de reprodução aleatória do cartão está usando esse RNG para produzir resultados aleatórios. Se você estiver escrevendo o RNG, use a análise estatística para provar sua distribuição; se estiver escrevendo o shuffler de cartões, verifique se há 52 cartões não repetidos em cada saída (é o melhor caso para teste por inspeção que você está usando o RNG).
fonte
Você pode confiar em geradores de números aleatórios seguros
Acabei de ter um pensamento horrível: você não está escrevendo seu próprio gerador de números aleatórios, está?
Supondo que você não seja, teste o código pelo qual você é responsável , e não o código de outras pessoas (como a
SecureRandom
implementação da sua estrutura).Testando seu código
Para testar se seu código responde corretamente, é normal usar um método de baixa visibilidade para produzir números aleatórios, para que ele possa ser facilmente substituído por uma classe de teste de unidade. Esse método substituído efetivamente zomba do gerador de números aleatórios e fornece controle total sobre o que é produzido e quando. Conseqüentemente, você pode exercitar completamente seu código, que é o objetivo do teste de unidade.
Obviamente, você verificará as condições das arestas e garantirá que o embaralhamento ocorra exatamente como o algoritmo determina, com as entradas apropriadas.
Testando o gerador seguro de números aleatórios
Se você não tiver certeza de que o gerador de números aleatórios seguro para o seu idioma não é verdadeiramente aleatório ou possui erros (fornece valores fora do intervalo, etc.), é necessário executar uma análise estatística detalhada da saída em várias centenas de milhões de iterações. Traçar a frequência de ocorrência de cada número e deve aparecer com igual probabilidade. Se os resultados se distorcerem de uma maneira ou de outra, você deve relatar suas descobertas aos designers da estrutura. Definitivamente, eles estarão interessados em solucionar o problema, uma vez que os geradores de números aleatórios seguros são fundamentais para muitos algoritmos de criptografia.
fonte
Bem, você nunca estará 100% certo, então o melhor que você pode fazer é que é provável que os números sejam aleatórios. Escolha uma probabilidade - diga que uma amostra de números ou itens será exibida x vezes, com um milhão de amostras, dentro de uma margem de erro. Execute a coisa um milhão de vezes e veja se está dentro da margem. Felizmente, os computadores facilitam esse tipo de coisa.
fonte
Para testar se uma fonte de números aleatórios está gerando algo que pelo menos tem a aparência de aleatoriedade, eu faria o teste gerar uma sequência bastante grande de bytes, gravá-los em um arquivo temporário e depois desembolsar para a ferramenta ent do Fourmilab . Insira a opção -t (concisa) para gerar um CSV fácil de analisar. Em seguida, verifique os vários números para ver se eles são "bons".
Para decidir quais números são bons, use uma fonte conhecida de aleatoriedade para calibrar seu teste. O teste quase sempre deve passar quando recebe um bom conjunto de números aleatórios. Como mesmo uma sequência verdadeiramente aleatória tem probabilidade de gerar uma sequência que parece não ser aleatória, não é possível fazer um teste que seja aprovado. Você apenas escolhe limites que tornam improvável que uma sequência aleatória cause uma falha no teste. A aleatoriedade não é divertida?
Nota: Você não pode escrever um teste que mostre que um PRNG gera uma sequência "aleatória". Você só pode escrever um teste que, se aprovado, indica alguma probabilidade de que a sequência gerada pelo PRNG seja "aleatória". Bem-vindo à alegria da aleatoriedade!
fonte
Caso 1: testando um shuffle:
Considere uma matriz [0, 1, 2, 3, 4, 5], embaralhe-a, o que pode dar errado? As coisas usuais: a) sem embaralhar, b) embaralhar 1-5, mas não 0, embaralhar 0-4, mas não 5, embaralhar e sempre gerar o mesmo padrão, ...
Um teste para pegar todos:
Embaralhe 100 vezes, adicione os valores em cada slot. A soma de cada slot deve ser semelhante à outra. Média / Stddev pode ser calculada. (5 + 0) /2=2,5, 100 * 2,5 = 25. O valor esperado é de cerca de 25, por exemplo.
Se os valores estiverem fora do intervalo, há uma pequena chance de que você tenha um falso negativo. Você pode calcular o quão grande é essa chance. Repita o teste. Bem - é claro que há uma pequena chance de o teste falhar duas vezes seguidas. Mas você não tem uma rotina que exclua automaticamente sua fonte, se o teste de unidade falhar, não é? Execute-o novamente!
Pode falhar 3 vezes seguidas? Talvez você deva tentar a sorte na loteria.
Caso 2: Rolar um dado
A pergunta dos dados é a mesma. Jogue os dados 6000 vezes.
fonte