Skip to content

Instantly share code, notes, and snippets.

@ldaniel
Created March 21, 2012 16:52
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ldaniel/2149474 to your computer and use it in GitHub Desktop.
Save ldaniel/2149474 to your computer and use it in GitHub Desktop.
Relação entre testes, design e métricas
Pessoal, gostaria de saber como vocês endereçam as questões que farei após explicar o cenário
abaixo. Pra mim, é muito claro que todas podem ser respondidas com um belo "depende", contudo,
vamos focar as respostas no que atenderia a maioria dos casos (lembrando que esse é um cenário
muito simplório).
-- CENÁRIO -----------------------------------------------------------------------------------------
Um desenvolvedor codificou inicialmente a classe (escrita em C#) no gist "exemplo_antes.cs".
Após aplicar um refactoring, ele chegou no resultado mostrado no gist "exemplo_depois.cs".
Algumas métricas foram coletadas antes e depois do refactoring (vide gist "métricas.txt").
Obs.: Esse código foi tirado daqui ->
http://whileicompile.wordpress.com/2010/09/14/what-is-too-simple-and-small-to-refactor-as-clean-code
-- QUESTÕES ----------------------------------------------------------------------------------------
1) De modo geral, podemos ter como prática que o set de testes deve, no mínimo, ter o mesmo número
de testes que o resultado do indicador CC (cyclomatic complexity)? Por exemplo, se um método tem
5 de CC, ele deve ter no mínimo 5 testes escritos para ele.
Notas: a) estou considerando apenas um assert por método de test.
b) não considerando a eficácia do teste escrito.
2) Membros privados podem ser ignorados nos testes? Devemos garantir os testes pelos membros
públicos observando a cobertura de código dos testes. Isso é suficiente?
Nota: sem entrar no mérito da necessidade ou não de 100% de cobertura. A intenção aqui é
deduzir uma relação entre métricas (total de membros privados/publicos) e testes.
3) Quando, através de métricas, chegamos a conclusão que o código ficou mais complexo, é uma boa
estratégia considerar LoC (lines of code) como indicador para comparar "antes" e "depois"? Que
outras métricas vocês considerariam?
4) Qual a melhor "unidade" para orientarmos a escrita de testes (de unidade, claro): método, classe,
assembly, assunto de negócio ou outra? (estou falando aqui de "Testes de Unidade": qual unidade
você comumente utiliza?)
/* Classe inicial, antes do refatoring */
using System;
namespace CleanCode.Before
{
public class WageCalculator
{
public static float Calculate(float hours, float rate, bool isHourlyWorker)
{
if (hours < 0 || hours > 80)
throw new ArgumentException();
float wages = 0;
if (hours > 40)
{
var overTimeHours = hours - 40;
if (isHourlyWorker)
wages += (overTimeHours * 1.5f) * rate;
else
wages += overTimeHours * rate;
hours -= overTimeHours;
}
wages += hours * rate;
return wages;
}
}
}
/* Classes geradas, após refactoring */
using System;
namespace CleanCode.After
{
public abstract class WageCalculatorBase
{
public float HoursWorked { get; protected set; }
public float HourlyRate { get; protected set; }
public WageCalculatorBase(float hours, float hourlyRate)
{
if (hours < 0 || hours > 80)
throw new
ArgumentOutOfRangeException("Hours must be between 0 and 80.");
HoursWorked = hours;
HourlyRate = hourlyRate;
}
public abstract float Calculate();
}
public class WageCalculatorForContractor : WageCalculatorBase
{
public WageCalculatorForContractor(float hours, float hourlyRate)
: base(hours, hourlyRate)
{
}
public override float Calculate()
{
return HoursWorked * HourlyRate;
}
public static float Calculate(float hours, float hourlyRate)
{
WageCalculatorForContractor payCalc = new
WageCalculatorForContractor(hours, hourlyRate);
return payCalc.Calculate();
}
}
public class WageCalculatorForEmployee : WageCalculatorBase
{
public WageCalculatorForEmployee(float hours, float hourlyRate)
: base(hours, hourlyRate)
{
}
public override float Calculate()
{
if (IsOvertimeRequired)
return CalculateWithOvertime();
return CalculateWithoutOvertime();
}
protected bool IsOvertimeRequired
{
get
{
return HoursWorked > 40;
}
}
protected float CalculateWithoutOvertime()
{
return HoursWorked * HourlyRate;
}
protected float CalculateWithOvertime()
{
float overTimeHours = HoursWorked - 40;
return (overTimeHours * 1.5f + 40) * HourlyRate;
}
public static float Calculate(float hours, float hourlyRate)
{
WageCalculatorForEmployee payCalc = new
WageCalculatorForEmployee(hours, hourlyRate);
return payCalc.Calculate();
}
}
}
Type Member CC LoC
--------------------------------------------------------------------
CleanCode.After........................................[18] [27]
WageCalculatorBase..............................(8) (9)
Calculate() 1 0
HourlyRate.get() 1 1
HourlyRate.set(float) 1 1
HoursWorked.get() 1 1
HoursWorked.set(float) 1 1
WageCalculatorBase(float, float) 3 5
WageCalculatorForContractor.....................(3) (5)
Calculate() 1 2
Calculate(float, float) 1 2
WageCalculatorForContractor(float,float) 1 1
WageCalculatorForEmployee.......................(7) (13)
Calculate() 2 3
Calculate(float, float) 1 2
CalculateWithoutOvertime() 1 2
CalculateWithOvertime() 1 3
IsOvertimeRequired.get() 1 2
WageCalculatorForEmployee(float, float) 1 1
CleanCode.Before........................................[6] [14]
WageCalculator..................................(6) (14)
Calculate(float, float, bool) 5 13
WageCalculator() 1 1
@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@mauricioaniche

Mau, agradeceria se você conseguisse os nomes e indicações completas das pessoas citadas, gosto muito do assunto e seria útil pra mim acrescentar essas experiências. Estou tentando, de maneira autodidata, estudar mining (até coloquei algo no github -> https://github.com/ldaniel/PoC_TextMining). Confesso que tenho tido dificuldade por não ter uma boa base de matemática (algo que já pensei resolver fazendo algum tipo de ensino formal/acadêmico).

Sobre buscar valores médios, isso não pode sofrer influência do tipo de sistema avaliado? Por exemplo, se avaliarmos a evolução do desenvolvimento de 100 frameworks, provavelmente, encontraremos em suas métricas várias tendências e comportamentos (consequentemente, um mesmo padrão de valor médio). Mas se levarmos isso para o desenvolvimento de código que trabalhe com parelelismo, essas médias estariam "furadas", não?

@tucaz
Copy link

tucaz commented Apr 11, 2012

Estou chegando tarde na discussão, mas não importa muito mesmo já que não vou agregar valor nenhum.

Tentei ler todos os comentários e acho que pelo lado técnico que o Leandro queria todo mundo estressou bastante os pontos possíveis. Então vou puxar outro lado da discussão.

Confesso que deu preguiça de ler a solução mais elegante (com polimorfismo), mas de fato ela é a mais elegante e deixa a intenção mais clara (pelo fato de ter nomes verbosos) e permitiria os testes mais bacanas. No entanto, pra um cenário de uma aplicação onde essa é a regra de negócio mais complexa que a aplicação tem, acho que é overkill. Como o @vquaiato falou, ficou faltando a parte do código que iria decidir qual estrategia aplicar.

No final, fica uma salada grande de classes pra resolver um problema, que na maioria das vezes nem vai ser resolvido dessa forma.

Acho que nunca vi um cenário como esse onde as regras são resolvidas só com informações que as classes possuem e que permita essa modelagem mais bacana (também, só trabalho com CRUD e Sharepoint então já viu né). Geralmente calculos deste tipo estão espalhados no meio de um monte de código de infra-estrutura, pois existe mais um bucado de variáveis envolvidas que vem de diversas fontes e testes ficam praticamente inviaveis.

De qualquer forma, acho muito bacana a idéia da discussão, apesar desse tipo de discussão ser mais téorica e instrutiva do que prática, já que infelizmente nossos problemas com código vem muito antes disso, em código básico.

TL;DR; Métricas e toda essa discussão de design são bem supimpa, mas no dia-a-dia os problemas são outros.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@ElemarJR @giggio

Fazendo um gancho com métricas, essa dissonância entre TCO e "o que o cliente precisa" pode ser atenuada se observarmos as métricas certas. Pense bem, tirar métricas através de ferramentas é algo muito barato (sob o ponto de vista de custo, tempo e esforço), então, chegar no balanço que o Giggio comentou é outro benefício oriundo de métricas.

@mauricioaniche
Copy link

mauricioaniche commented Apr 11, 2012 via email

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@tucaz

Nunca é tarde para entrar numa discussão entre amigos que AMAM o que fazem. =D

Concordo com o que você disse: os problemas do dia-a-dia são outros. O meu primeiro post da série sobre métricas enfatizava isso colocando a utilidade de utilizar esse tipo ferramenta na mão do arquiteto (cargo ou papel, não importa). Creio que essa seja a figura adequada para focar nesse tipo de análise, pois no geral, os desenvolvedores estão mais focados na parte e não no todo.

@juanplopes
Copy link

@mauricioaniche

Hmm, cálculo fiscal complexo. Muita coisa se explica, então. :)

@tucaz

Discordo que os problemas do dia-a-dia sejam outros. Isso depende muito de como você resolve os problemas. Não sou escravo de métricas, mas meu painel de code coverage / cyclomatic complexity do IntelliJ fica aberto 24/7.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@mauricioaniche

Valeu!

Depois que estiver tudo publicado, quero ver o resultado desse estudo. será produto? se for, cria uma licença para fins não comerciais. =D

@juanplopes
Copy link

@ldaniel

Putz, Leandro, não há como eu discordar mais disso. Desenvolvedores estão focados na parte e não no toco? Desenvolvedores não são responsáveis por design bem feito? Que espécie de desenvolvedores são esses?

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

Pior que olhar essas métricas deveria ser o mínimo dentro das obrigações de qualquer desenvolvedor. =\

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

os que eu vejo por aí. =(

@juanplopes
Copy link

@ldaniel

Métricas são ferramentas, não obrigação. Obrigar o desenvolvedor a alcançar certas métricas é pedir para a métrica ser corrompida.

Obrigação do desenvolvedor saber escrever código claro, desacoplado e coeso. E cabe a ele encontrar a melhor forma de fazer isso.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

Desenvolvedores estão focados na parte e não no toco?

O que tentei dizer é que desenvolvimento é uma atividade com foco distinto de arquitetura.

@juanplopes
Copy link

@ldaniel

Existe um certo nível de arquitetura que até concordo que alguns desenvolvedores não estejam tão engajados quanto outros. Apesar de crer que essa divisão de responsabilidades só gera impedância nos processos pelos interesses divergentes.

Mas isso que estamos discutindo aqui não é arquitetura, é design. E o mínimo que eu espero de um desenvolvedor é ser capaz de modelar algo sem precisar de uma forminha de bolo.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

as métricas citadas são muito baratas de serem coletadas. não vejo problema algum de se tornarem parte do cotidiano do desenvolvedor, é quase como termos a janela quickwatch durante o debug... deveria ser algo natural.

@juanplopes
Copy link

@ldaniel

Sim, realmente são bastante baratas, eu uso. Mas quando se torna obrigação, é da natureza humana deturpar a métrica. Então o foco passará a ser bater a métrica, e não gerar código de qualidade (que é o objetivo inicial).

@tucaz
Copy link

tucaz commented Apr 11, 2012

@juanplopes, @mauricioaniche:

Quem faz nossa realidade somos nós, disso eu sei, mas ainda assim vou insistir neste ponto. Ando sem muita vontade de cantar uma bela canção, mas nosso ambiente mercadológico hoje em dia não ajuda muito. Tentando fazer um trabalho de team lead, a maioria das vezes eu lido com gente que tem dificuldade em fazer o FizzBuzz. Outro dia passei como teste pra um desenvolvedor "Senior" o Mars Rover da TW e o cara insistiu até o final que o exercicio estava errado e não tinha jeito de resolver. Sei que esse mimimi todo não ajuda, mas acho que na maioria dos casos achamos que o problema esta na falta de conhecimento OO, no processo errado ou na falta de patterns ou sei-la-mais-oque.

No entanto, acredito fortemente que o problema esta no básico. O filtro de entrada hoje é muito baixo.

Falei todo esse bla bla simplesmente pelo seguinte. Métricas são importantes, úteis pra caramba e bem legais, mas não são práticas na maioria dos cenários então acho que não vale muito a pena discutir isso. É a mesma coisa que perguntar se podemos ir pra Lua. O caboclo vem e responde que sim e tudo mais, que é fácil. No entanto ninguém vai. Ou seja, na prática mesmo, apesar de ser possível, não é. Acho que a melhor analogia é o may e can em inglês. We can, but may not.

@ldaniel:

Concordo contigo. Acho que esse tipo de coisa é pra um dos poucos caras que ta ali distante do código (no dia-a-dia), mas olhando por cima pra empresa como um todo. Não sei não, mas IMO esse papel ta cada vez mais raro já que a qualidade base de tudo está cada vez pior, ainda mais com o monte de tecnologia nova que sai e que só complica mais a vida dos ja não tão bons desenvolvedores.

Bão, ignorem o velho chato aqui resmungando da vida, porque faz todo sentido o assunto.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

E o mínimo que eu espero de um desenvolvedor é ser capaz de modelar algo sem precisar de uma forminha de bolo.

Métrica não é forminha de bolo, certo? O que vc quis dizer com isso?

@viniciushana
Copy link

Complementando o que o @juanplopes disse:

Métricas são números, que sem compreensão e contexto, não significam nada. Pior ainda, podem fomentar decisões questionáveis, igual estatísticas. Por isso mesmo que vejo que são ferramentas e não objetivos.
E justamente, entender a importância e como elas podem ajudar é algo muito interessante para se espalhar por aí, pois muita gente não conhece ou não entende.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

Bom, acho que concordamos então. São ferramentas úteis. Ninguém obriga um desenvolvedor a usar a janelinha quickwatch, mas esperamos um código de qualidade e com o mínimo de bugs.

@mauricioaniche
Copy link

mauricioaniche commented Apr 11, 2012 via email

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@tucaz não consigo deixar de lembrar das discussões do DNAD em 2008, quando tudo começou. poxa, era exatamente isso aqui. que coisa legal.

... ah saudosismo... =P

@juanplopes
Copy link

@ldaniel

Métricas não são forminhas de bolo, mas estabelecer que elas (muitas vezes sozinhas) são obrigatórias para um bom design é nocivo.

Mas o que eu estava falando, na verdade, são desenvolvedores que não pensam em nada além da receita que seguem para desenvolver uma funcionalidade. Por isso tantas perguntas sobre DDD, repositórios, camadas, etc. no DNA. Por que a grande massa não quer ter que pensar. Quer "comprar" uma modelagem pronta na "prateleira das boas práticas" e usar.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@tucaz @mauricioaniche

pois é, eu continuo batendo na tecla de que métricas (as mais comuns, pelo menos) são muito baratas para serem tiradas (custo, tempo, esforço etc). acho que talvez o problema seja um pouco de tudo:

  • zilhões de tecnologias e possibilidades de estudo hoje em dia
  • banalização do skill necessário para um desenvolvedor
  • alta oferta de empregos e poucos profissionais (bons) disponíveis
  • sei lá, tudo pode ser usado como "muleta" hoje em dia...

a impressão que tenho quando olho para o grupo que está reunido aqui respondendo essas questões, é que não representamos os "fodões" da área de TI. caramba, fazer um trabalho bem feito deveria ser a meta básica de qualquer um (independente de contexto, pelo brio mesmo, poxa). no entanto, tenho que concordar com o Tuca quando ele se desanima com o atual mercado.

@ldaniel
Copy link
Author

ldaniel commented Apr 11, 2012

@juanplopes

sim, concordo com tudo.

@tucaz
Copy link

tucaz commented Apr 11, 2012

@mauricioaniche:

É "impossível" porque o desenvolvedor que ta ali do meu lado todo dia tem dificuldade em compreender como funciona um ORM, javascript básico e até HTML e CSS. Outro dia pedi pra um cara mudar uma classe CSS e ele não sabia como fazer. E isso não é nem aqui no Brasil somente. Esse caso em especifico aconteceu nos EUA então esse problema não é exclusividade nossa.

Imagina falar pra ele que o código dele tem uma complexidade enorme por causa dos caminhos que os IF's geram? Ou pedir pro cara refatorar pra deixar mais coeso? Não é prático.

Se olharmos pro indivíduo dentro do grupo, talvez até seja possível, mas disseminar esse comportamento pra um grupo de desenvolvedores que tem outras motivações é impraticável.

@ldaniel, mais uma vez vou ter que concordar com você que a discussão está interessante. Fazia tempo que eu não via algo assim, onde o pessoal não da carteirada pra fugir do assunto e provar um ponto. Way to go!

@juanplopes
Copy link

@tucaz

Nunca fiz um estudo formal para tentar confirmar isso, mas tenho a sincera impressão que desenvolvedores despreparados assim geram mais prejuízo que valor nos produtos em que desenvolvem.

Não que desenvolvedores iniciantes não possam trabalhar, mas por esse motivo sou a favor de code review ou (para os mais xp-fans) pair programming. É claro que se a proporção de desenvolvedores iniciantes for absurdamente maior que a de experientes, o custo disso fica inviável. E talvez seja irreal na maior parte das empresas com o mercado que temos.

@ldaniel

Curiosamente, sem querer puxar sardinha, eu vejo que a comunidade Java tem muito mais apego às métricas que a .NET. Em .NET você tem dificuldade até em encontrar ferramentas adequadas para tirar essas métricas.

@mauricioaniche
Copy link

mauricioaniche commented Apr 11, 2012 via email

@tucaz
Copy link

tucaz commented Apr 11, 2012

@juanplopes,

compartilho da sua impressão. Tanto é que ultimamente adotei a postura de aceitar mais trabalho (não tanto assim :P) ao invés de contratar gente despreparada que vai gerar mais trabalho do que já temos.

@mauricioaniche,

foi por isso que falei que sou velho chato. Com certeza não é motivo pra desanimar. Essa imagem de desanimado minha é mesmo uma imagem. Apesar dos pesares, ainda tenho energia pra tentar promover um monte de coisa bacana.

Quanto a equipes boas, sei que elas existem. Não em tantos lugares, mas também não estão em extinção. Só não consegui unir o útil ao agradável ainda ($$$). Infelizmente empresas ruins pagam mais, afinal sempre precisam de gente pra resolver problemas, né?

@juanplopes
Copy link

@tucaz

Vem pro Rio. Estamos contratando!

Brincadeira...

Não, é sério. Pode vir. :P

@marcioalthmann
Copy link

Acho que cheguei só um pouco atrasado kkk, mesmo recebendo no twitter o link uma refatoração de um código legado não me permitiu participar :D

@ldaniel interessante a discussão, a única refatoração que gostei foi a do @juanplopes e agora minhas respostas.

1- Sim, no mínimo, quando preciso testar códigos assim geralmente verifico a cobertura de código para garantir que passei por todo lugar.

2 - Essa depende mesmo, testo métodos privados mas ai tenho que concordar com o @juanplopes talvez tenha um probleminha em precisar testar esses caras :)

3 - Cara não sei, acho que LoC não vai ajudar a dizer que ficou mais complexo, muita linha de código simples é melhor que pouca linha de código complexo :D. E ai depende do "feeling" que o @vquaiato disse. Agora pensando... e se além de LoC considerar número de testes? Acho que nesse exemplo o número de testes para o código refatorado acabaria maior do que com o código sem refatorar.

4 - Classe

Mais uma vez parabéns, sensacional a discussão aqui, todos foram geniais

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment