Melhorando a expressividade dos testes unitários com JUnitXtension – Parte 1

23.Outubro.2009
Qual dos códigos é mais simples de ler?

Código A

assertFalse(foo.equals(bar));

assertFalse(foobar == 2);

Código B

assertNotEquals(foo, bar);

assertNotEquals(foobar, 2);

Inicialmente criado apenas como um projeto para me auxiliar no trabalho a escrever testes mais simples de ler, resolvi compartilhar no Google Code a biblioteca, atualmente na versão 0.1.1.

A principal motivação foi de criar novas asserções que melhorem a expressividade, mesmo que redundantes, para que o desenvolvedor não tenha que escrever códigos auxiliares limitados à API padrão do JUnit.

Internamente o próprio JUnit trabalha dessa maneira. Praticamente todas as demais asserções são abstrações sobre assertTrue, no máximo adicionando alguma formatação diferenciada para que o resultado e um teste falho seja legível.

Ao invés de, por exemplo, escrevermos assertFalse(0 == 1), podemos escrever assertNotFalse(0, 1). Ao invés de assertTrue(x > 0), podemos simplesmente escrever assertGreaterThanZero(x) e assim por diante.

Outra vantagem diz respeito à cobertura de testes. Em alguns casos, quando se usa assertFalse(x == 0), o algoritmo que calcula a cobertura vai informar que essa linha foi apenas parcialmente testada. Na verdade queremos apenas testar se x é igual a zero, mas se você persegue os 100% de cobertura, esse tipo de problema obriga a criar código extra apenas para manter a cobertura do código satisfatória. Utilizando assertNotEquals(0, x), ou o teste passa ou não passa, reduzindo para apenas duas as opções possíveis, limpando o código e aumentando automagicamente a cobertura.

Se interessou pela idéia? Você encontra os mesmos problemas e quer conhecer mais? A biblioteca está na versão 0.1.1 e pode ser encontrada no Google Code. Críticas construtivas são sempre bem vindas.

Keep testing ;-)

P.S.: Agradecimentos públicos à minha esposa e ao Fábio Serra pelas revisões nesse post


RESTful Rails automático

02.Julho.2009
Solução OO para fazer sua aplicação Rails trabalhar de modo RESTful:

application_controller.rb:

  def index
    begin
      if request.post?
        create
      elsif request.get?
        read
      elsif request.put?
        update
      elsif request.delete?
        delete
      end
    rescue NameError
      respond_to do |format|
        format.html {render :text => "<h1>Forbidden</h1>", :status => 403}
      end
    end
  end

E no arquivo routes.rb:

  map.connect ':controller/:id'
  # map.connect ':controller/:action/:id'

Como funciona?

Uma aplicaçao Rails RESTful funciona, basicamente, utilizando o método HTTP como verbo e a URI como substantivo.

Ao invés de algo do tipo http://application:3000/user/view/1, com REST você usa http://application:3000/user/1 para pegar os dados, atualizar ou apagar, mudando apenas o método HTTP da chamada.

Porém, por padrão, o Rails entende as requisições como sendo /controller/action/id, por isso a alteração no arquivo routes.rb, para fazer com que o segundo parâmetro seja o ID, já que quem define a action é o próprio método HTTP.

Dependendo do método utilizado, será executada uma action padronizada que será escrita no seu próprio controller, usando o conceito de sobrescrita de métodos. Caso você não escreva qualquer uma das actions, será exibida uma mensagem de erro padrão, permitindo que você foque somente no desenvolvimento do que for necessário.


Testes unitários – you’re doing it wrong

14.Maio.2009
Se você:
- Precisa de um método public static void Main(String[] args) para executar seus testes
- Depende de uma ordem pré-determinada para executá-los
- Caso um teste falhe, todos falham na seqüência
- Dependa de um banco de dados, de uma conexão ou de qualquer recurso externo para executar o teste
- Precise rodar os testes na mão toda vez que quiser saber se estão passando
- Escreve testes sem assert com freqüência

Sinto dizer, mas você não está usando testes unitários.

Update: Estou amadurecendo melhor a idéia, mas relendo isso aqui, e conversando com alguns desenvolvedores, fiquei pensando: “OK, você está fazendo errado. Então como é o certo? Por que está errado?”. Fiquei em dívida com vocês. Mas pretendo publicar algo mais decente sobre isso em breve.


Ruby ao resgate

06.Março.2009
Estou meio sumido, fazendo o projeto andar apesar dos gols contra, e chafurdando no Rails.

Fica uma dica para quem também está começando a se divertir no Ruby. Para executar um processo que pode causar uma exceção, podemos proteger o código usando rescue, assim como em C++/Java temos o try/catch:

begin
  file = open("arquivo")
rescue
  msg = "Arquivo não pode ser aberto"
  logger.error msg
  show_message :error => msg
end

Simples, não?


Integração Contínua

27.Fevereiro.2009
Instalei em minha máquina de trabalho um servidor de integração contínua.

Quando se trabalha sozinho, num projeto pequeno, isso realmente não faz muito sentido. Porém, trabalhando com um recurso a 500km de distância, e mais dois recursos remotos cuidando de um projeto relacionado, cuja alteração indevida pode quebrar o funcionamento da parte em que trabalho, esse tipo de ferramenta começa a fazer sentido.

O modo correto, antes de mais nada, é instalar esse servidor numa máquina que possa ser facilmente acessada por qualquer parte interessada. Minha máquina de trabalho, obviamente, só fica disponível quando estou conectado na rede da empresa, para quem estiver na mesma condição.

Instalei o Apache Continuum rodando como um NT Service (sim, eu uso Windows no trabalho), acessível como uma aplicação web a partir da porta 8080.

Para quem se interessou, recomendo a leitura do artigo do Martin Fowler sobre Integração Contínua. Estou organizando uma documentação para compartilhar com a equipe e pretendo divulgá-la aqui.

P.S.: Sim, eu sei que tem Bamboo, mas comunicação ainda é um ponto crítico a ser desenvolvido.


Mais um bom motivo para não se usar comentários

17.Fevereiro.2009
Você está trabalhando feliz e sorridente naquele projeto de prazo estourado, com aquela API que não funciona, e eis que de repente você encontra a maravilha de código abaixo, devidamente comentada, claro:

t.each(function(){
$(this).bind("click",function(){ //可更换为其他动画形式
t.remove(tbo); // 在此可添加多组选项卡

Que bom, hein? O fonte está comentado, e imagino que deva ter as devidas explicações sobre o que faz cada parte.

Se convenceu agora de que comentários em código, além de dificultarem a leitura e se tornarem um vício, são ruins e devem ser evitados?

Escreva um código que seja esclarecedor por si só, sem sujeira, e seja mais feliz.


Primeiros passos em Rails

16.Fevereiro.2009
Ambiente devidamente configurado, fui eu meter as caras no Rails, framework para agilizar e divertir (???) o desenvolvedor, segundo informado no próprio site.

Algumas coisas me empurraram para o Rails, na ordem:

- Curiosidade: acredite, um programador sem curiosidade não é nada além de um repetidor de tarefas.
- Custo: desenvolvendo em Grails eu teria que contratar uma JVM dedicada para hospedar minha aplicação. Isso gerou um custo que deixou o cliente insatisfeito. Como quem manda é ele, fui obrigado a buscar uma solução mais econômica.
- Compatibilidade: Boa parte da aplicação já estava feita em Grails, então eu precisaria aproveitar o máximo possível. Devido ao fato do Grails ter sido criado usando o Rails como base, a migração teoricamente seria menos traumática.
- Produtividade: Esse é o ponto chave do Rails. Em termos de custo de hospedagem, pode ser comparado ao PHP, mas com certeza é uma ferramenta que provê muito mais recursos do que o dinossauro da Zend. Por exemplo, eu posso criar uma tela de cadastro no Rails utilizando poucas linhas de código, enquanto no PHP não existe nenhum suporte nativo a isso, apesar de ferramentas como PHPScaffold.

Tive alguns problemas chatos tentando utilizar o ActiveScaffold, mas esse é mais um projeto que sofre da clássica doença do “fiz, mas não testei”. Não funcionou de jeito nenhum com o Rails 2.2.2 que estou utilizando. Instalei o Streamlined e, por enquanto, tudo está caminhando tranquilamente.


NetBeans em inglês

12.Fevereiro.2009
Toda boa idéia acaba desbundando para algo porco, mal feito ou que simplesmente causa a morte de milhões de pessoas, como a televisão, o Orkut ou a bomba atômica.

Alguém, algum dia, teve a idéia de traduzir o NetBeans. Claro, afinal existem milhares de programadores preguiçosos que não sabem trabalhar com um botão Run, mesmo ele sendo aquela setinha verde de sempre, ou um menu File, mesmo ele estando no mesmo lugar desde 1985. Tudo bem, foi uma boa idéia para quem precisa. Bastaria eu baixar a versão em inglês e ignorar as traduções.

Aí alguém avacalhou com a idéia: ‘por que não alteramos o idioma automaticamente, de acordo com as configurações na máquina do usuário?‘. E lá se foi meu direito de escolha para usar o IDE no idioma que eu bem entender.

Para resolver esse problema, já que eu não quero trabalhar com Rails usando Visualizações, Controladores nem Modelagens, você tem que abrir a pasta onde o NetBeans foi instalado, e editar o arquivo etc/netbeans.conf e adicionar na opção netbeans_default_options a sequência -J-Duser.region=US -J-Duser.language=en.

Pronto, abra o NetBeans e aproveite a interface como ela realmente é, com seus devidos Models, Views e Controllers. Ou então, se possível, use o Eclipse, o que eu pessoalmente prefiro.


Link

03.Fevereiro.2009

Mapear uma id tipo string no Grais – base de dados legado

Pelo que entendi, é de um blog novo sobre Grails. Convém acompanhar.


Mudando a página inicial padrão no Grails

30.Janeiro.2009
Ao se criar uma aplicação padrão em Grails, a página inicial é o index.gsp, que fica na pasta web-app.

Por algum motivo que não interessa para o momento, a minha aplicação estava ficando torta com essa configuração. Comecei a misturar página estática com gambiarra para suprir a ausência de um controller.

Como a gambiarra é um bumerangue, e para não fugir do MVC, é possível resolver isso alterando o arquivo grails-app/conf/UrlMappings.groovy, da forma abaixo:

class UrlMappings {
  static mappings = {
    "/$controller/$action?/$id?"{
      constraints {
        // apply constraints here
      }
    }
    "500"(view:'/error')
    "/" {
      controller = "app"
      action = "login"
    }
  }
}

O controller app, que é onde eu centralizo o fluxo da aplicação, fica da seguinte maneira:

class AppController {
  def login = {
    render (view: 'login')
  }
}

Aí você pergunta: ‘mas para que tanta complicação se a action apenas renderiza uma view?’

De fato, é apenas esforço inútil. Porém, se eu quiser que o usuário, devidamente autenticado, seja redirecionado para a tela principal da aplicação sem ter que passar novamente pelo login, posso alterar o código para que a action resolva isso:

  def login = {
    if(session.user)
      render (view: 'telaInicial')
    else
      render (view: 'login')
  }

Fica ao gosto do freguês, claro.