Os testes de unidade ajudariam o Citigroup a evitar esse erro caro?


86

Eu li sobre esse problema: O bug de programação custa ao Citigroup US $ 7 milhões após transações legítimas confundidas com dados de teste por 15 anos .

Quando o sistema foi introduzido em meados da década de 1990, o código do programa filtrou todas as transações que receberam códigos de ramificação de três dígitos de 089 a 100 e usaram esses prefixos para fins de teste.

Mas em 1998, a empresa começou a usar códigos alfanuméricos de filiais à medida que expandia seus negócios. Entre eles estavam os códigos 10B, 10C e assim por diante, que o sistema tratava como estando dentro do intervalo excluído e, portanto, suas transações foram removidas de quaisquer relatórios enviados à SEC.

(Acho que isso ilustra que o uso de um indicador de dados não explícito é ... sub-ideal. Teria sido muito melhor preencher e usar uma Branch.IsLivepropriedade semanticamente explícita .)

Além disso, minha primeira reação foi "testes de unidade teriam ajudado aqui" ... mas eles ajudariam?

Li recentemente Por que a maioria dos testes de unidade é desperdiçada com interesse e, portanto, minha pergunta é: como seriam os testes de unidade que teriam falhado na introdução de códigos alfanuméricos de ramificação?



17
Parece que eles também perderam um teste de integração que verificou a quantidade de transações exportadas para a SEC. Se você criar um recurso de exportação, isso seria uma verificação razoável.
11686 Luc Lucen

31
O autor do artigo parece não entender o teste de unidade. Algumas afirmações são simplesmente ridículas ( "é improvável que testes de unidade testem mais de um bilionésimo da funcionalidade de qualquer método" ), outros destruiriam a chance de obter regressões ( "observe os testes que nunca falharam em um ano e considere jogá-los fora " ). Ou sugestões como "transformar testes de unidade em asserções" , que deveriam alterar testes com falha para exceções de tempo de execução?
Groo

25
@gnat Eu não li o link externo, e eu ainda encontrei esta pergunta significativa
Jeutnarg

23
Pelo que vale, discordo de tudo em "Por que a maioria dos testes de unidade é desperdício". Eu escreveria uma refutação, mas essa margem é muito pequena para contê-la.
Robert Harvey

Respostas:


19

Você está realmente perguntando "os testes de unidade teriam ajudado aqui?", Ou você está perguntando "algum tipo de teste poderia ter ajudado aqui?".

A forma mais óbvia de teste que teria ajudado é uma asserção de pré-condição no próprio código, de que um identificador de ramificação consiste apenas de dígitos (supondo que essa seja a suposição invocada pelo codificador ao escrever o código).

Isso pode ter falhado em algum tipo de teste de integração e, assim que os novos IDs de ramificação alfanuméricos são introduzidos, a asserção explode. Mas isso não é um teste de unidade.

Como alternativa, pode haver um teste de integração do procedimento que gera o relatório da SEC. Esse teste garante que todo identificador de filial real relate suas transações (e, portanto, requer entrada do mundo real, uma lista de todos os identificadores de filial em uso). Portanto, isso também não é um teste de unidade.

Não consigo ver nenhuma definição ou documentação das interfaces envolvidas, mas pode ser que os testes de unidade não tenham detectado o erro porque a unidade não estava com defeito . Se a unidade puder assumir que os identificadores de ramificação consistem apenas de dígitos, e os desenvolvedores nunca tomarem uma decisão sobre o que o código deve fazer caso não o faça, então eles não devemescreva um teste de unidade para impor um comportamento específico no caso de identificadores que não sejam dígitos, porque o teste rejeitaria uma implementação hipotética válida da unidade que manipulava corretamente os identificadores alfanuméricos de ramificação e você geralmente não deseja escrever um teste de unidade que impeça a validade futuras implementações e extensões. Ou talvez um documento escrito há 40 anos definido implicitamente (por meio de um intervalo lexicográfico no EBCDIC bruto, em vez de uma regra de classificação mais amigável ao ser humano) que 10B é um identificador de teste porque, na verdade, cai entre 089 e 100. Mas então Há 15 anos, alguém decidiu usá-lo como um identificador real; portanto, a "falha" não está na unidade que implementa corretamente a definição original: está no processo que não percebeu que 10B é definido como um identificador de teste e, portanto, não deve ser atribuído a uma ramificação. O mesmo aconteceria no ASCII se você definisse 089 - 100 como um intervalo de teste e, em seguida, introduzisse um identificador 10 $ ou 1,0. Acontece que no EBCDIC os dígitos vêm depois das letras.

Um teste de unidade (ou possivelmente um teste funcional) que é concebívelpode ter salvado o dia, é um teste da unidade que gera ou valida novos identificadores de filial. Esse teste afirmaria que os identificadores devem conter apenas dígitos e seria gravado para permitir que os usuários dos identificadores de ramificações assumissem o mesmo. Ou talvez exista uma unidade em algum lugar que importe identificadores de ramificação reais, mas nunca os veja, e que possa ser testada por unidade para garantir que rejeite todos os identificadores de teste (se os identificadores tiverem apenas três caracteres, poderemos enumerá-los todos e comparar o comportamento de o validador ao do filtro de teste para garantir que eles correspondam, o que lida com a objeção usual aos testes pontuais). Então, quando alguém mudasse as regras, o teste de unidade teria falhado, pois contradiz o novo comportamento exigido.

Como o teste foi realizado por um bom motivo, o ponto em que você precisa removê-lo devido a requisitos de negócios alterados torna-se uma oportunidade para alguém receber o trabalho ", encontre todos os lugares no código que se baseiam no comportamento que queremos mudança". É claro que isso é difícil e, portanto, pouco confiável, de modo algum garantiria salvar o dia. Mas se você capturar suas suposições em testes das unidades das quais você está assumindo propriedades, então você se deu uma chance e, portanto, o esforço não é totalmente desperdiçado.

Concordo, é claro, que se a unidade não tivesse sido definida em primeiro lugar com uma entrada "de formato engraçado", não haveria nada a ser testado. As divisões de espaço de nomes complicados podem ser difíceis de testar adequadamente, porque a dificuldade não está em implementar sua definição engraçada, mas em garantir que todos entendam e respeitem sua definição engraçada. Essa não é uma propriedade local de uma unidade de código. Além disso, alterar algum tipo de dados de "uma sequência de dígitos" para "uma sequência alfanumérica" ​​é semelhante a fazer com que um programa baseado em ASCII manipule o Unicode: não será simples se o seu código estiver fortemente associado à definição original e quando o tipo de dados é fundamental para o que o programa faz, geralmente é fortemente associado.

é um pouco perturbador pensar que é um esforço amplamente desperdiçado

Se às vezes seus testes de unidade falharem (enquanto você está refatorando, por exemplo) e, ao fazer isso, fornecer informações úteis (sua alteração está errada, por exemplo), o esforço não foi desperdiçado. O que eles não fazem é testar se o seu sistema funciona. Portanto, se você estiver escrevendo testes de unidade, em vez de ter testes funcionais e de integração, poderá usar seu tempo de maneira otimizada.


As afirmações são boas!

3
@nocomprende: como Reagan tinha, "confie, mas verifique".
21816 Steve Jessop

1
Eu ia dizer também "testes de unidade ruins!" mas achei que a maioria das pessoas perderia a referência à Animal Farm e começaria a me criticar em vez de pensar no que eu estava dizendo (respostas bruscas são ineficazes), mas não disse isso. Talvez uma pessoa mais inteligente e com maior erudição possa fazer isso.

2
"Todos os testes estão passando, mas alguns são mais aprovados que outros!"
Graham

1
o teste é um arenque vermelho. Esses caras simplesmente não sabiam como "código de ramificação" foi definido. Isso seria como se a agência postal dos EUA não soubesse que estava mudando a definição de código postal ao adicionar quatro dígitos.
Radarbob

120

Os testes de unidade poderiam ter detectado que os códigos de ramificação 10B e 10C foram classificados incorretamente como "ramificações de teste", mas acho improvável que os testes para essa classificação de ramificação tenham sido extensos o suficiente para detectar esse erro.

Por outro lado, as verificações pontuais dos relatórios gerados poderiam ter revelado que 10B e 10C ramificados estavam constantemente ausentes dos relatórios muito antes dos 15 anos em que o bug agora podia permanecer presente.

Finalmente, esta é uma boa ilustração do motivo pelo qual é uma má idéia misturar dados de teste com os dados reais de produção em um banco de dados. Se eles tivessem usado um banco de dados separado que contém os dados de teste, não haveria necessidade de filtrar isso dos relatórios oficiais e seria impossível filtrar demais.


80
+1 Testes de unidade nunca pode compensar decisões de design pobres (como a mistura de teste e dados reais)
Jeutnarg

5
Embora seja melhor evitar misturar dados de teste com dados reais, pode ser difícil validar um sistema de produção, se isso exigir modificação de dados reais. Por exemplo, é uma má idéia validar um sistema bancário modificando o total de contas bancárias em produção. Usar intervalos de código para designar significado é problemático. Um atributo mais explícito dos registros provavelmente teria sido uma escolha melhor.
21416 JimmyJames

4
@ Voo Acho que há uma suposição tácita de que exista um nível de complexidade ou exigência de confiabilidade em que testar o sistema de produção implantado é considerado valioso ou necessário. (Considere o quanto poderia dar errado por causa de uma variável de configuração incorreta.) Pude ver que esse é o caso de uma grande instituição financeira.
jpmc26

4
@ Voo eu não estou falando sobre testes. Eu estou falando sobre validação do sistema. Em um sistema de produção real, há muitas maneiras de falha que não têm nada a ver com código. Se você estiver colocando um novo sistema bancário em produção, poderá ter algum problema no banco de dados ou na rede etc., impedindo que as transações sejam aplicadas às contas. Eu nunca trabalhei em um banco, mas tenho certeza de que é desaprovado começar a modificar contas reais com transações falsas. Isso deixa você com a criação de contas falsas ou espera e ora.
21716 JimmyJames

12
@JimmyJames Na área da saúde, é comum copiar periodicamente o banco de dados de produção no ambiente de teste para executar testes em dados o mais próximo possível da realidade; Eu acho que um banco pode fazer o mesmo.
dj18

75

O software tinha que lidar com certas regras de negócios. Se houvesse testes de unidade, os testes de unidade teriam verificado se o software tratava as regras de negócios corretamente.

As regras de negócios foram alteradas.

Aparentemente, ninguém percebeu que as regras de negócios haviam mudado e ninguém mudou o software para aplicar as novas regras de negócios. Se houvesse testes de unidade, esses testes de unidade teriam que ser alterados, mas ninguém o faria porque ninguém percebeu que as regras de negócios haviam mudado.

Portanto, não, testes de unidade não teriam percebido isso.

A exceção seria se os testes de unidade e o software tivessem sido criados por equipes independentes, e a equipe que fazia os testes de unidade alterasse os testes para aplicar as novas regras de negócios. Então os testes de unidade teriam falhado, o que, esperançosamente, teria resultado em uma alteração do software.

Obviamente, no mesmo caso, se apenas o software fosse alterado e não os testes de unidade, os testes de unidade também falhariam. Sempre que um teste de unidade falha, isso não significa que o software está errado, significa que o software ou o teste de unidade (às vezes os dois) estão errados.


2
É possível ter equipes diferentes, onde uma está trabalhando no código e outra nos testes de "unidade"? Como isso é possível? ... Estou refatorando meu código o tempo todo.
14743 Sergio

2
@Sergio de uma perspectiva, a refatoração muda internamente enquanto preserva o comportamento - por isso, se o teste é escrito de uma maneira que testa o comportamento sem depender de internos, não é necessário atualizar.
Daenyth 14/07/16

1
Eu já vi isso acontecer várias vezes. O software está em produção sem reclamações; então, de repente, os usuários explodem com reclamações de que não funcionam mais e estão gradualmente falhando ao longo dos anos. Isso é o que acontece quando você decidir ir e mudar seus procedimentos internos, sem seguir o processo de notificação padrão ...
Brian Knoblauch

42
"As regras de negócios mudaram" é a observação crítica. Os testes de unidade validam que você implementou a lógica que você pensou que implementou , e não que sua lógica estava correta .
21416 Ryan Cavanaugh

5
Se eu estiver certo sobre o que aconteceu, é improvável que testes de unidade para capturar isso sejam escritos. O princípio básico para a seleção de testes é testar alguns casos "bons", outros "ruins" e casos entre colchetes. Nesse caso, você testaria "099", "100" e "101". Como "10B" foi coberto pelos testes "rejeitar não-números" no sistema antigo e é maior que 101 (e também é coberto pelo teste) no novo sistema, não há razão para testá-lo - exceto que em EBCDIC, "10B" classifica entre "099" e "100".
21416 Mark

29

Não. Esse é um dos grandes problemas dos testes de unidade: eles levam você a uma falsa sensação de segurança.

Se todos os seus testes forem aprovados, isso não significa que seu sistema esteja funcionando corretamente; isso significa que todos os seus testes estão passando . Isso significa que as partes do seu design nas quais você pensou conscientemente e escreveu testes estão funcionando como você pensava que funcionaria conscientemente, o que realmente não é tão importante assim: essas eram as coisas nas quais você estava prestando muita atenção então é muito provável que você acertou! Mas não serve para capturar casos em que você nunca pensou, como este, porque você nunca pensou em escrever um teste para eles. (E, se você tivesse, estaria ciente de que isso significava que alterações de código eram necessárias e você as teria alterado.)


17
Meu pai costumava me perguntar o seguinte: por que você não pensou no que não pensava? (Apenas ele costumava confundir dizendo "Se você não sabe, pergunte !") Mas como eu sei que não sei?

7
"Isso significa que as partes do seu design nas quais você pensou conscientemente e escreveu testes estão funcionando como você pensou que elas funcionariam". Exatamente certo. Essas informações são inestimáveis ​​se você estiver refatorando ou se algo mudar em algum outro lugar do sistema que quebre suas suposições. Os desenvolvedores com uma falsa sensação de segurança simplesmente não entendem as limitações do teste de unidade, mas isso não torna o teste de unidade uma ferramenta inútil.
Robert Harvey

12
@MasonWheeler: Como você, o autor pensa que o teste de unidade deve, de alguma forma, provar que seu programa funciona. Não faz. Deixe-me repetir: o teste de unidade não prova que seu programa funciona. O teste de unidade prova que seus métodos cumprem seu contrato de teste, e isso é tudo o que faz. O restante do artigo cai, porque se baseia nessa única premissa inválida.
Robert Harvey

5
Naturalmente, os desenvolvedores que têm essa crença falsa ficarão desapontados quando o teste de unidade falhar completamente, mas isso é culpa do desenvolvedor, não do teste de unidade, e isso não invalida o valor genuíno que o teste de unidade fornece.
Robert Harvey

5
o_O @ sua primeira frase. Os testes de unidade oferecem uma falsa sensação de segurança ao codificar, como colocar as mãos no volante, uma falsa sensação de segurança ao dirigir.
djechlin

10

Não, não necessariamente.

O requisito original era usar códigos de ramificação numéricos; portanto, um teste de unidade teria sido produzido para um componente que aceitasse vários códigos e rejeitasse qualquer 10B. O sistema teria sido passado como funcionando (como estava).

Em seguida, o requisito teria mudado e os códigos atualizados, mas isso significaria que o código de teste de unidade que forneceu os dados incorretos (que agora são dados válidos) precisaria ser modificado.

Agora assumimos que, as pessoas que gerenciam o sistema saberiam que esse era o caso e alterariam o teste de unidade para lidar com os novos códigos ... mas, se soubessem que isso estava ocorrendo, também teriam sabido alterar o código que tratava desses códigos. códigos de qualquer maneira .. e eles não fizeram isso. Um teste de unidade que originalmente rejeitasse o código 10B teria dito felizmente "está tudo bem aqui" quando executado, se você não soubesse atualizar esse teste.

O teste de unidade é bom para o desenvolvimento original, mas não para o sistema, especialmente 15 anos após os requisitos terem sido esquecidos.

O que eles precisam nesse tipo de situação é um teste de integração de ponta a ponta. Um onde você pode passar os dados que espera trabalhar e ver se funcionam. Alguém teria notado que seus novos dados de entrada não produziram um relatório e depois investigaram mais.


Spot on. E o principal (apenas?) Problema com testes de unidade. Me salvou redigindo minha própria resposta, como eu teria dito precisamente a mesma coisa (mas provavelmente pior!) :) #
Lightness Races in Orbit

8

O teste de tipo (o processo de testar invariantes usando dados válidos gerados aleatoriamente, como exemplificado pela biblioteca de testes Haskell QuickCheck e várias portas / alternativas inspiradas por ele em outros idiomas) pode ter percebido esse problema, o teste de unidade quase certamente não teria acontecido. .

Isso ocorre porque quando as regras para a validade dos códigos de ramificação foram atualizadas, é improvável que alguém tenha pensado em testar esses intervalos específicos para garantir que funcionassem corretamente.

No entanto, se o teste de tipo estivesse em uso, alguém deveria, no momento em que o sistema original foi implementado, escrever um par de propriedades, uma para verificar se os códigos específicos para ramificações de teste foram tratadas como dados de teste e outra para verificar se nenhum outro código foram ... quando a definição do tipo de dados para o código da ramificação foi atualizada (o que seria necessário para permitir testar se alguma das alterações no código da ramificação de dígito para numérico funcionava), esse teste começaria a testar valores em o novo intervalo e provavelmente teria identificado a falha.

Obviamente, o QuickCheck foi desenvolvido pela primeira vez em 1999, por isso já era tarde demais para entender esse problema.


1
Eu acho que é mais normal chamar esse teste de propriedade e, é claro, é possível escrever um teste de propriedade que ainda passaria com essa alteração (embora eu ache que é mais provável que você escreva um teste que possa encontrá-lo)
jk.

5

Eu realmente duvido que o teste de unidade faria diferença para esse problema. Parece uma daquelas situações de visão de túnel porque a funcionalidade foi alterada para oferecer suporte a novos códigos de ramificação, mas isso não foi realizado em todas as áreas do sistema.

Usamos testes de unidade para projetar uma classe. É necessário executar novamente um teste de unidade apenas se o design tiver sido alterado. Se uma unidade específica não for alterada, os testes de unidade inalterados retornarão os mesmos resultados de antes. Os testes de unidade não mostrarão os impactos das alterações em outras unidades (caso contrário, você não está escrevendo testes de unidade).

Você só pode detectar razoavelmente esse problema via:

  • Testes de integração - mas você precisaria adicionar especificamente os novos formatos de código para alimentar várias unidades do sistema (ou seja, eles só mostrariam o problema se os testes originais incluíssem as ramificações agora válidas)
  • Teste de ponta a ponta - a empresa deve executar um teste de ponta a ponta que incorpore formatos de código de filial antigos e novos

Não ter testes de ponta a ponta suficientes é mais preocupante. Você não pode confiar no teste de unidade como seu teste SOMENTE ou PRINCIPAL para alterações do sistema. Parece que só foi necessário que alguém gerasse um relatório nos formatos de código de filial recém-suportados.


2

Uma afirmação incorporada ao tempo de execução pode ter ajudado; por exemplo:

  1. Crie uma função como bool isTestOnly(string branchCode) { ... }
  2. Use esta função para decidir quais relatórios filtrar
  3. Reutilize essa função em uma asserção, no código de criação de ramificação, para verificar ou afirmar que uma ramificação não é (não pode ser) criada usando esse tipo de código de ramificação‼
  4. Tenha essa afirmação ativada no tempo de execução real (e não "otimizada, exceto na versão do desenvolvedor para depuração do código")‼

Veja também:


2

A conclusão disso é falhar rapidamente .

Não temos o código, nem temos muitos exemplos de prefixos que são ou não são prefixos de ramificação de teste de acordo com o código. Tudo o que temos é o seguinte:

  • 089 - 100 => ramo de teste
  • 10B, 10C => ramo de teste
  • <088 => ramificações presumivelmente reais
  • > 100 => ramificações presumivelmente reais

O fato de o código permitir números e seqüências de caracteres é mais do que um pouco estranho. Obviamente, 10B e 10C podem ser considerados números hexadecimais, mas se todos os prefixos forem tratados como números hexadecimais, 10B e 10C ficarão fora do intervalo de teste e serão tratados como ramificações reais.

Isso provavelmente significa que o prefixo é armazenado como uma sequência, mas tratado como um número em alguns casos. Aqui está o código mais simples que consigo pensar que replica esse comportamento (usando C # para fins ilustrativos):

bool IsTest(string strPrefix) {
    int iPrefix;
    if(int.TryParse(strPrefix, out iPrefix))
        return iPrefix >= 89 && iPrefix <= 100;
    return true; //here is the problem
}

Em inglês, se a sequência for um número e estiver entre 89 e 100, é um teste. Se não é um número, é um teste. Caso contrário, não é um teste.

Se o código seguir esse padrão, nenhum teste de unidade detectaria isso no momento em que o código foi implantado. Aqui estão alguns exemplos de testes de unidade:

assert.isFalse(IsTest("088"))
assert.isTrue(IsTest("089"))
assert.isTrue(IsTest("095"))
assert.isTrue(IsTest("100"))
assert.isFalse(IsTest("101"))
assert.isTrue(IsTest("10B")) // <--- business rule change

O teste de unidade mostra que "10B" deve ser tratado como um ramo de teste. O usuário @ gnasher729 acima diz que as regras de negócios mudaram e é isso que a última afirmação acima mostra. Em algum momento, essa afirmação deveria ter mudado para uma isFalse, mas isso não aconteceu. Os testes de unidade são executados no tempo de desenvolvimento e construção, mas depois não há nenhum momento.


Qual é a lição aqui? O código precisa de alguma maneira de sinalizar que recebeu entrada inesperada. Aqui está uma maneira alternativa de escrever esse código que enfatiza que ele espera que o prefixo seja um número:

// Alternative A
bool TryGetIsTest(string strPrefix, out bool isTest) {
    int iPrefix;
    if(int.TryParse(strPrefix, out iPrefix)) {
        isTest = iPrefix >= 89 && iPrefix <= 100;
        return true;
    }
    isTest = true; //this is just some value that won't be read
    return false;
}

Para quem não conhece C #, o valor retornado indica se o código foi ou não capaz de analisar um prefixo da string especificada. Se o valor de retorno for verdadeiro, o código de chamada pode usar a variável isTest out para verificar se o prefixo da ramificação é um prefixo de teste. Se o valor de retorno for falso, o código de chamada deve relatar que o prefixo fornecido não é esperado e a variável isTest out não tem sentido e deve ser ignorada.

Se você concorda com as exceções, pode fazer o seguinte:

// Alternative B
bool IsTest(string strPrefix) {
    int iPrefix = int.Parse(strPrefix);
    return iPrefix >= 89 && iPrefix <= 100;
}

Essa alternativa é mais direta. Nesse caso, o código de chamada precisa capturar a exceção. Em ambos os casos, o código deve ter alguma maneira de relatar ao chamador que ele não esperava um strPrefix que não pudesse ser convertido em um número inteiro. Dessa forma, o código falha rapidamente e o banco pode encontrar rapidamente o problema sem o embaraço da SEC.


1

Tantas respostas e nem mesmo uma citação de Dijkstra:

O teste mostra a presença, não a ausência de bugs.

Portanto, depende. Se o código foi testado corretamente, provavelmente esse bug não existiria.


-1

Eu acho que um teste de unidade aqui teria garantido que o problema nunca existisse.

Considere, você escreveu a bool IsTestData(string branchCode)função.

O primeiro teste de unidade que você escreve deve ser para cadeia nula e vazia. Em seguida, para cadeias de comprimento incorretas e depois para cadeias não inteiras.

Para fazer todos esses testes passarem, você precisará adicionar a verificação de parâmetros à função.

Mesmo se você testar apenas os dados 'bons' 001 -> 999 sem pensar na possibilidade de 10A, a verificação de parâmetros forçará a reescrever a função quando você começar a usar alfanuméricos para evitar as exceções que serão lançadas


1
Isso não teria ajudado - a função não foi alterada e o teste não começaria a falhar devido aos mesmos dados de teste. Alguém teria que pensar em mudar o teste para fazê-lo falhar, mas se eles tivessem pensado nisso, provavelmente teriam pensado em mudar a função também.
Hulk

(Ou talvez eu estou faltando alguma coisa, como eu não tenho certeza do que você quer dizer com "verificação de parâmetro")
Hulk

A função seria forçada a lançar uma exceção para seqüências de caracteres não inteiras na ordem para passar no teste de unidade de caso de borda simples. Daí código de produção seria de erro se você começou a usar códigos de filiais alfanuméricos sem specificaly programação para eles
Ewan

Mas a função não teria usado alguma IsValidBranchCodefunção-para executar esta verificação? E essa função provavelmente teria sido alterada sem a necessidade de modificar o IsTestData? Portanto, se você estiver testando apenas 'bons dados', o teste não teria ajudado. O teste de caso de borda teria que incluir algum código de ramificação agora válido (e não simplesmente alguns ainda inválidos) para começar a falhar.
Hulk

1
Se a verificação estiver em IsValidCode, para que a função passe sem sua própria verificação explícita, sim, é possível perdê-la, mas teríamos um conjunto extra de ainda mais testes, validadores simulados etc. ainda mais chances de "específicos". test numbers "
Ewan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.