TDD apenas em teoria


29

Há pouco mais de um ano, tive a sorte de poder tirar uma folga de 9 meses do trabalho. Decidi que, naquele tempo, aperfeiçoaria minhas habilidades em C #. Comecei a trabalhar em vários projetos e me forcei a seguir o TDD.

Foi um processo bastante esclarecedor.

Foi difícil no começo, mas com o tempo aprendi a escrever código mais testável (que, como se vê, tende a ser mais código SOLID) e, no processo, também aprimorei minha habilidade em design de OO.

Agora estou de volta à força de trabalho e estou percebendo algo estranho.

Eu prefiro não seguir TDD.

Acho que o TDD me deixa mais lento e torna mais difícil projetar um aplicativo limpo.

Em vez disso, adotei uma abordagem ligeiramente (massivamente) diferente:

  1. Escolha uma fatia vertical do trabalho
  2. Desenvolver um protótipo funcional
  3. Refatorar até que tudo esteja agradável e arrumado
  4. Relaxe e aprecie o código lindamente sólido e testável que escrevi.

Você deve ter notado que a etapa 1 não foi "definir a superfície pública do meu alvo de teste" e a etapa 2 não foi "testar o bejesus a partir dessa superfície pública". Você também deve ter notado que nenhuma das etapas envolve testes. Estou escrevendo código testável, mas não estou testando ... ainda.

Agora, gostaria de deixar claro que na verdade não estou renunciando a nenhum tipo de teste. O código que estou escrevendo funciona . Funciona porque estou testando manualmente.

Também gostaria de deixar claro que também não estou renunciando a todos os testes automatizados. É aqui que meu processo é diferente. E é por isso que estou fazendo essa pergunta.

TDD em teoria. Não na prática.

Meu processo evoluiu um pouco e encontrei um equilíbrio entre TDD e nenhum teste que considero muito produtivo e também razoavelmente seguro. É o seguinte:

  1. Implemente uma fatia de trabalho vertical de trabalho tendo em mente os testes, mas não escreva nenhum teste.
  2. Se no caminho (por exemplo, um mês depois), essa fatia precisa ser modificada
    1. Escreva Testes de Unidade, Testes de Integração, Testes de Comportamento, etc., que garantam que a fatia do trabalho esteja correta
    2. Modifique o código
  3. Se essa fatia não precisar de modificação,
    1. Fazer nada

Simplesmente mudando o fardo de escrever testes de antes de escrever o código para antes de modificar o código, eu pude produzir muito mais código funcional. E, quando passo a escrever testes, escrevo muito menos deles, mas abro quase o mesmo terreno (maior ROI).

Gosto desse processo, mas estou preocupado que ele possa não ser bem dimensionado. Seu sucesso depende de desenvolvedores serem diligentes em escrever testes antes de mudarem as coisas. E isso parece ser um risco muito grande. Mas, o TDD tem o mesmo risco.

Então, eu estou indo para o inferno, ou isso é uma forma comum de codificação e testes pragmáticos?

Eu gostaria de continuar trabalhando dessa maneira. O que posso fazer para que esse processo funcione a longo prazo?

Nota:

Sou o único desenvolvedor dos meus projetos e sou responsável por tudo: coleta de requisitos, design, arquitetura, teste, implantação etc. Suspeito que é por isso que meu processo está funcionando.


2
Parece pico e estabilizar sem sempre fazer o estabilizador se If that slice doesn't need modification. lizkeogh.com/2012/06/24/beyond-test-driven-development
RubberChickenLeader

13
Você está descobrindo algo sobre TDD que eu suspeitava há muito tempo, que o primeiro mantra de teste é uma ferramenta de aprendizado excepcionalmente boa , mas não é design, apenas encoraja um bom design. No final, o que você deseja são códigos testáveis ​​e testes de unidade que forneçam uma boa cobertura de código e espelhem os requisitos do software; como você está descobrindo, você pode conseguir isso sem escrever os testes primeiro, se praticar princípios de design sensatos.
Robert Harvey

5
Sim, e escrever testes primeiro basicamente dobra seu trabalho de prototipagem.
Robert Harvey

3
isso significa que eu estava mentindo sobre isso ser "um pouco".
MetaFight

1
"E, quando eu escrevo testes, escrevo muito menos deles, mas abro quase tanto (ROI mais alto)" Quando você diz que escreve muito menos, você quer dizer apenas porque está testando apenas o código ' está mudando ou você está dizendo que, de alguma forma, está cobrindo o mesmo pedaço de código (testado) com menos testes do que se tivesse usado o TDD?
precisa

Respostas:


6

Para que o processo funcione a longo prazo, eu escreveria os testes quando o código estiver sendo escrito.

O que pode parecer contradizer sua abordagem. No entanto, você fez a pergunta, então eu darei a você a minha opinião:

Você não precisa escrever os testes antes do código. esqueça essa pureza. No entanto, você quer escrever os testes em todo esse tempo.
Depois que o código está funcionando, você o aprimorou um pouco, eliminou alguns bugs (estamos falando de uma escala de horas aqui), então você está em um ponto de conhecimento máximo sobre o que o código está fazendo. Este é um ótimo momento para escrever testes que capturam seu conhecimento.

Deixar isso para depois significa que o conhecimento (naturalmente) diminuirá com o tempo.

Isso também significa que, se você sair e mais alguém assumir o controle, não terá a dívida técnica imediata de não ter documentado (por meio de testes) o que faz o quê.

Acima de tudo, "algum dia" pode não chegar. Você pode ser atropelado por um ônibus ou pode embarcar no ônibus para novas aventuras.

Por fim, o teste manual não é escalável e frequentemente não abrange todos os dispositivos usados ​​pelo usuário final.


Acho que gosto da sua abordagem sugerida e provavelmente a aplicarei quando puder. Meu trabalho é muito fragmentado, portanto, "uma escala de horas" nem sempre é possível. Infelizmente também estou apoiando, por isso muitas vezes sou retirado do meu trabalho para ajudar a combater incêndios :) Mas, isso é vida.
MetaFight

A questão, porém, é que o amanhã nunca chega, sempre há o próximo recurso. E o que você escolherá fazer; escreva o próximo recurso ou escreva os testes para o que você acabou de "terminar"?
Andy

9

Embora o TDD seja difícil de implementar 100%, há uma falha na sua abordagem

  1. Implementar uma fatia vertical de trabalho

    1,1 1 ano passa ....

    1.2 Um novo desenvolvedor começa a trabalhar no projeto

  2. Se essa fatia precisar de modificação

    2.3 Analisar os nomes e parâmetros do método de estilo 'Clean Coding' 'GetUnicorn (colourOfUnicorn)'

    2.4 Leia os comentários xml 'Obtém um unicórnio de ouro (para cavalgar) (obvs)'

    2.5 Caçar o desenvolvedor original

    2.6 Espero que eles se lembrem do que o código deve fazer

    2.7 Faça com que expliquem tudo

  3. Escreva Testes de Unidade, Testes de Integração, Testes de Comportamento, etc. que esperam garantir que a fatia de trabalho esteja correta

  4. Modifique o código

Acho que você está certo ao identificar que os testes de unidade realmente mostram seu valor quando são necessárias modificações.


2
Ei, eu escrevo código auto-documentado! Minhas aulas têm uma responsabilidade e são, portanto, fáceis de entender. Ninguém vai precisar me caçar :)
MetaFight 4/15

7
@MetaFight e, se o fizerem, será fácil identificar em cima do unicórnio vivo de ouro maciço!
precisa saber é o seguinte

3
Eu o chamo de Goldicorn.
MetaFight 04/04

Em uma nota mais séria, sim, você tem razão. Eu considerei registrar histórias de "teste de dívida" para poder pagar um pouco quando minha carga de trabalho for menor.
MetaFight 04/04

4
Se o código for escrito bem, mas tiver um erro, provavelmente será bastante fácil entender o que o desenvolvedor original pretendia realizar lendo o código, e o novo desenvolvedor poderá adicionar os testes necessários. O único problema é que a maioria dos desenvolvedores pensa que está escrevendo um bom código. "Bom" depende muito da sua perspectiva e experiência como programador. Então, há uma tensão nisso que precisa ser gerenciada.
Phil

4

Concordo com Daniel Hollinrake e Ewan, que o primeiro ponto-chave do motivo pelo qual o seu teste apenas se modificar funciona até agora é:

I am the sole developer on my projects and I am responsible for everything

e que um provável segundo ponto-chave é:

you're producing nice clean code

Eu não acho que o TDD traga um enorme aumento de produtividade para programadores únicos e talvez não melhore enormemente a qualidade do seu código se você já estiver escrevendo um bom código limpo.

No entanto, o TDD certamente melhorará a qualidade do código de programadores ruins / inexperientes / obsoletos, especialmente quando chegar a hora de modificar o código sem interromper nada mais. E ainda mais se a pessoa que modificou o código não for a mesma pessoa que escreveu o código originalmente ou vários meses se passaram.

Em outras palavras, acho que o TDD é uma boa prática para melhorar a qualidade do seu código (como você se reconhece), mas também (e mais importante) um tipo de proteção quando você está trabalhando com programadores médios ou medíocres (por exemplo, de um departamento ou empresa diferente), que é uma situação muito mais comum do que trabalhar sozinho.


1
Eu acho que parte do problema é que pode haver apenas 1 programador, mas a base de código geralmente cresce com o tempo e o que funcionou (para testes) quando era pequeno não continua funcionando à medida que aumenta.
Michael Durrant

3

Para mim, a principal coisa parece ser esta:

Sou o único desenvolvedor dos meus projetos e sou responsável por tudo: coleta de requisitos, design, arquitetura, teste, implantação etc. Suspeito que é por isso que meu processo está funcionando.

Isso funciona para você e você está produzindo um bom código limpo (presumo!). A única coisa que eu diria que você precisa fazer é criar um equipamento de teste para que outros desenvolvedores possam entrar e ter confiança em fazer alterações. Além disso, o equipamento de teste garante consistência no comportamento do código.

Eu acho que sua abordagem é semelhante à minha. Normalmente sou o único desenvolvedor em meus projetos. Descobri que uma apreciação do TDD me permitiu escrever funções menores e código mais limpo, mas adiciono testes enquanto escrevia o código como um equipamento de teste. Dessa forma, à medida que o código evolui e a funcionalidade muda, posso estar razoavelmente confiante ao fazer alterações.

Uma razão secundária para escrever testes é que eu sinto que eles são uma forma de documentação. Eles podem explicar meus raciocínios por trás de por que uma função foi criada. Mas aqui, estou pensando mais sobre o desenvolvimento orientado a comportamentos.


Eu diria que o conjunto de testes chegaria em 4º lugar para passar para outros desenvolvedores - documentos de requisitos, diagramas de arquitetura e documentos de design seriam muito mais importantes para comunicar assuntos do que vários testes de unidade.
Gbjbaanb

É um ponto justo, mas, infelizmente, na minha experiência, em quase todos os projetos em que trabalhei na documentação, quando existe, estão desatualizados ou incompletos.
Daniel Hollinrake

1
O mesmo aqui, e é por isso que os desenvolvedores devem perceber a importância de documentar as coisas e não escrever mais código na forma de testes! Talvez precisemos de ferramentas para permitir uma melhor geração de documentação (isto é, não é apenas a bonita formatação das assinaturas de métodos) a partir de comentários de código e tickets de requisitos.
gbjbaanb

Editei minha resposta um pouco em resposta aos seus comentários. Obrigado.
Daniel Hollinrake

1
@gbjbaanb Se eu puder ajudar, eu gostaria de evitar escrever documentos de requisitos, diagramas de arquitetura e documentos de design. Isso ocorre porque eles tendem a ficar obsoletos muito rápido. No meu caso, tenho muita sorte, pois gerencio muitos aplicativos pequenos com muito poucas responsabilidades. Isso torna a documentação de requisitos e arquitetura um pouco exagerada. E os projetos são pequenos o suficiente para que o design geral seja claro. O que estou documentando, no entanto, é como os sistemas interagem, como implantá-los e como monitorar sua saúde.
MetaFight

3

O Teste de Unidade trata do problema de manter o código. Embora existam pessoas que dizem que escrevem mais rapidamente código com TDD do que sem, não estou surpreso que você consiga escrever mais código novo sem escrever testes.

Os problemas que eu posso ver com a prática de escrever testes antes de você alterá-lo:

Muitas vezes preciso fazer alterações com pressa

Embora você possa economizar tempo no geral, apenas escrevendo testes quando precisar deles, nem todo o tempo é igual. Gastar 2 horas escrevendo testes para economizar 1 hora quando estou no modo de crise - vale totalmente a pena.

É mais fácil escrever testes ao mesmo tempo em que escrevo o código

Para escrever corretamente os testes de unidade, você precisa entender o código que estou testando. Costumo usar o teste de unidade como um exercício de entendimento, mas o teste de unidade do código existente pode ser demorado, porque a compreensão do código existente é demorada. Compare isso ao escrever testes enquanto você escreve o código e você o encontrará muito mais rápido porque você já entende o código - você acabou de escrever!


A definição de Michael Feathers do código legado é código sem testes. Independentemente de você concordar com a definição dele, fica claro que uma parte substancial do custo da modificação do código existente está garantindo que ele ainda funcione conforme o esperado, geralmente nem é claro qual é o comportamento esperado.

Escrever testes de unidade compensa as compensações que custam, codificando uma compreensão do que é o comportamento correto, além de fornecer uma maneira fácil para o "futuro nós" verificar se o comportamento ainda está correto.


2

Esta é uma boa pergunta, e FWIW eu jogarei meus dois centavos.

Há cerca de um ano, eu estava codificando no Salesforce, uma plataforma que tinha um mecanismo entranhado que o obrigava a não necessariamente escrever testes antes de codificá- lo , mas o obrigava a escrever testes em geral.

A maneira como funcionou foi que o sistema o forçaria a escrever testes e faria um cálculo do número de linhas do seu código que foram testadas em uma porcentagem. Se todo o código em toda a sua instância de produção cair abaixo de 75% testado. O Salesforce não funcionará mais.

O resultado final disso foi que toda vez que você fazia algo no Salesforce, precisava escrever ou atualizar testes. Embora eu tenha certeza de que isso tem um enorme impacto na participação de mercado do Salesforce, em termos da vida de um desenvolvedor , foi uma enorme dor de cabeça .

Na maioria das vezes, você estava apenas tentando obter um ticket pequeno e, em seguida, o teste entra e dobra seu tempo de desenvolvimento, para um recurso que você sabe que funciona.

Então, o estranho conceito de TDD varreu nosso departamento, até nossos bancos de dados. Nossos arquitetos queriam realizar testes completos em todos os aspectos do nosso departamento de TI. Ligeira dor na bunda, encontre uma dor ainda maior na bunda.

Naquela época, o TDD realmente não fazia sentido para mim, e mesmo agora ainda não faz. Muitas das funcionalidades que escrevi na minha função atual ocorrem em um mecanismo semelhante ao que você mencionou: em fatias verticais que refino até que funcionem. Quando eu estava no antigo cargo, e agora ainda não sei o que meu código vai fazer até que eu o escreva , então a ideia de que eu possa escrever testes para conduzir o código é que vou escrever apenas. não faz sentido para mim, é complicado, e principalmente uma perda de tempo.

Tudo isso dito, testes são coisas maravilhosas e mágicas que fazem tudo certo no mundo . Eles corrigem seu código, garantem que seu aplicativo faça o que você pensa e geralmente tudo mais suave. A questão então não é se você escreve seus testes antes de codificar ou depois de codificar, a questão é quanto tempo você vai se comprometer com o teste. Esse é o verdadeiro problema, pelo menos na minha experiência em desenvolvimento de software. Testar exige tempo e dinheiro e você deve fazê-lo dentro da estrutura de interesses concorrentes.

E assim, em geral, eu concordo com você: o TDD na prática é um pouco estranho e complicado. Nesse ponto, você precisa ter em mente o que funciona melhor na sua situação atual . Se você estiver escrevendo um código crítico, verifique se ele foi testado em geral. Se você tiver tempo, experimente o TDD e veja se isso acrescenta algo ao processo.


2
Eu sinto que estamos a uma geração de idiomas de acertar o TDD. Eu acho que agora o TDD está "aparafusado" na maioria dos idiomas com estruturas xUnit. Em algum momento, será construído apenas como a codificação é feita - e não separada. Como você definiria uma classe, e imediatamente os stubs para todos os testes seriam gerados, juntamente com alguns subconjuntos dos próprios testes (aqueles que poderiam ser facilmente determinados pelas próprias classes / métodos).
Calphool 6/06/15

3
@Calphool Nós têm realmente integrado algumas coisas facilmente testáveis para a língua! Chamamos isso de digitação estática . O Rust vai além com a verificação de empréstimos para testar ainda mais bugs. Mas a maioria dos testes é específica para essa classe exata ("se eu clicar no botão, o widget fica vermelho") - como o compilador / IDE possivelmente saberia que você testaria isso?
user253751

1
@immibis: Talvez estendendo ainda mais a verificação de tipo. Talvez o conceito de "o widget fique vermelho" se torne um conceito de primeira classe que possa ser deduzido de alguma forma a partir do código. Não pretendo ter as respostas, apenas sinto que o TDD ainda é novo o suficiente para não ter sido totalmente integrado à evolução da linguagem.
Calphool

1
O Salesforce especificamente tem todos os testes errados: eles exigem a realização de testes, mas tornam ridiculamente difícil escrever testes de qualidade . Parece ótimo na teoria, mas na prática faz com que os desenvolvedores desejem arrancar os olhos com colheres.

1

Eu não poderia recomendar sua abordagem.

Se eu usar sua abordagem, seria, por exemplo, o seguinte (a casa é a aplicação):

  1. Começo a construir uma casa para minha família como pedreiro com algum conhecimento ou como iniciante.
  2. Conheço os requisitos, como quartos para crianças, quarto de hóspedes e começo a construir minha casa "protótipo".
  3. Algumas vezes depois, sua casa de "protótipo" está pronta.
  4. Começo a procurar se a estrutura é estável o suficiente manualmente. Então eu pego muitos pesos e os trago para as diferentes salas do primeiro andar. Para garantir que, quando estou sentado em uma sala com minha família, o teto não se quebre. Mas ele quebra e eu começo a refatorar. Primeiro limpando toda a massa. Em seguida, construa-o novamente e teste-o novamente manualmente até que esteja estável o suficiente.
  5. Do que eu moro com minha família. Está tudo bem.
  6. Uma mariposa depois, meus primos e meus pais estão vindo nos visitar. Mas antes que eles possam entrar em nossa casa, eles precisam pagar um arquiteto e um engenheiro civil para garantir que o teto não se quebre quando nos sentamos em uma das salas do primeiro andar.
  7. O arquiteto e o engenheiro civil têm muito trabalho porque não têm nada para começar. Então eles precisam entrar na minha casa e ver como eu a construo.
  8. E, novamente, não é estável o suficiente. Então eles têm que refatorar o chão do primeiro andar.
  9. Mas depois disso tudo está bem e tudo pode entrar em segurança na minha casa.

Portanto, sua abordagem custa muito tempo e muito conhecimento antes que eu construa a casa com sua abordagem. Ou leva muito tempo! Também não é muito gentil deixar outros testes de gravação para o seu código quando os requisitos forem alterados.

Portanto, existe uma abordagem melhor sem programar um "protótipo" e depois iniciar a refatoração. Em vez de programar um protótipo "faça um Design com UML do seu aplicativo da seguinte maneira.

  1. Crie um diagrama UseCase. Você pode usar o draw.io para começar.
  2. Em seguida, crie um diagrama EPK com base no seu UseCases para determinar o comportamento. (COMPORTAMENTO do seu aplicativo) Mais rápido para refatorar do que refatorar um protótipo codificado. Especialmente quando você é iniciante.
  3. Crie um diagrama de classe. (ESTRUTURA da sua aplicação)
  4. Determine onde você pode ter problemas na implementação do comportamento.
  5. Escreva para isso um protótipo simples com talvez 10 ou 20 linhas de código para determinar como você pode implementar esse comportamento. Bom para iniciantes. Ou assista a um tutorial, veja o código-fonte de outros aplicativos de exemplo por aí. Como eles resolveram isso.
  6. Do que começar a codificar. Primeiro, realize os testes de sucesso do seu UseCase. Isso pode ser feito de maneiras diferentes. Primeiro, crie toda a estrutura necessária para o teste e esse UseCase. Ao usar o Enterprise Architekt, a estrutura pode ser gerada para você. Com base em seus diagramas. Ou crie a estrutura enquanto liga o teste. Portanto, nenhum erro de compilação aparece. Mencione aqui que você SOMENTE precisa testar o COMPORTAMENTO de seu aplicativo. Os UseCases que você possui.
  7. Implementar o comportamento do seu UseCase.
  8. Após o êxito do UseCases, comece a escrever testes para as exceções. E é sempre bom quando você vê as cores verdes quando seus testes são válidos;)
  9. E você terminou.

Certamente, essa abordagem também precisa de algum conhecimento em UML, mas é rápida de aprender. E é sempre mais rápido renomear uma classe ou mover setas em um digram do que em seu IDE. Mas aprender o uso de estruturas de teste será mais exaustivo no começo. Best está aqui para testar os projetos de código aberto e verificar como eles funcionam. Mas quando você possui um aplicativo orientado a teste, o próximo aplicativo será muito mais rápido. E acho que é bom saber que tudo funciona bem.

Então, acabei de votar nas abordagens, porque elas consomem muito tempo para iniciantes e não são boas, afinal. Para ter uma borda limpa entre sua estrutura e o comportamento, você pode usar o Design orientado a domínio e em Organizar muito domínio com dois pacotes (um pacote denominado estrutura e o outro chamado comportamento). Também para seus testes. exemplo simples, confira este exemplo escrito em java.


1

O código que estou escrevendo funciona. Funciona porque estou testando manualmente.

Você testou manualmente todas as ramificações possíveis de suas condições após poucas alterações? Quanto tempo leva no ciclo de feedback do seu teste manual. Quão perto você fica do ciclo de feedback dos testes automatizados.

Testes automatizados (não importa primeiro ou não) fazem com que você vá rápido - fornecendo um loop de feedback mais rápido no seu código.

Tem certeza de que se lembrará de testar alguma condição manualmente após seis meses - não diga que documentará todas as condições importantes a serem testadas - porque escrever tipo de documentação / comentário é igual a escrever teste (documentação executável)

  • Escolha uma fatia vertical do trabalho

  • Desenvolver um protótipo funcional

  • Refatorar até que tudo esteja agradável e arrumado

E novamente: durante a refatoração, você testou manualmente toda a lógica afetada pela refatoração? Quanto tempo leva para testar a refatoração de alterações? Se a refatoração quebrar algum código, quanto tempo leva para você encontrar um motivo para a quebra?

  • Relaxe e aprecie o código lindamente sólido e testável que escrevi.

O código bonito e limpo de que você gostou é muito subjetivo. Seu código pode ser limpo e razoável para você. O melhor método para verificar se seu código é realmente legível, compreensível e testável são testes e análises de código feitas por outros desenvolvedores.

Você encontrou o seu caminho muito produtivo apenas porque você é apenas desenvolvedor trabalhando com o código e, eu acho, porque você está começando a trabalhar neste projeto (quantos anos você trabalha nesse projeto? 6 a 8 meses?).
Você ainda se lembra de tudo que escreveu e pode reconhecer uma razão para possíveis problemas. Tenho certeza de que você começará a escrever testes do início após 2 a 3 anos do seu projeto - porque deseja ter certeza de que não esquece nada.


0

Se você nunca cometer erros, não precisará realmente de testes. A maioria dos desenvolvedores comete erros, mas se você nunca cometer e está confiante de que nunca cometerá erros no futuro (e você é o único no projeto), não há realmente nenhuma razão para perder tempo escrevendo testes.

Mas sua solução está na metade do caminho, porque você propõe escrever testes ao alterar o código, mas, ao mesmo tempo, seu método assume que você nunca cometerá erros ao decidir em quais partes do código escrever testes. Isso só funciona se você sempre entender perfeitamente quais áreas uma alteração pode afetar. Eu acho que muitos desenvolvedores comuns (não você, é claro!) Experimentaram fazer uma alteração e, em seguida, um teste em algum lugar inesperado falha, porque você cometeu um erro.

É claro que uma boa arquitetura, os princípios do SOLID etc. devem impedir que isso aconteça, mas a maioria dos desenvolvedores não é perfeita, e é por isso que os testes em todo o sistema são valiosos.


É claro que uma boa arquitetura, princípios SOLID etc. devem impedir que isso aconteça - não. Sistemas complexos têm partes que afetam outras partes, é exatamente assim. por exemplo modificando a gramática Antlr em Rubberduck pode facilmente fazer o pretendido modificado trabalho parte perfeitamente, ao quebrar 45 outras características. Sem testes completos, não há como saber, e você teria que ser louco para testar todos os casos manualmente. Se eu mudar alguma coisa no resolvedor e os testes 987 forem interrompidos, sei que fiz algo errado e o que isso afetou.
Mathieu Guindon
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.