Testando Javascript no Rails

Continuando os estudos com Javascript e Ruby, esses dias tive um problema bem chato: existe uma gem (muito boa, por sinal) para Rails chamada “cells“, que basicamente cria uma camada, semelhante a “mini controllers” para coisas específicas em Rails (tipo “sidebar”, “carrinhos de compra”, “menus” e outras funcionalidades que são, basicamente, fragmentos de “views”, normalmente feitas com a combinação “partials+helpers” mas que ficam relativamente difíceis de testar). Junto com o Cells, foi criado o Apotomo, uma gem para criar “widgets”, e aí que está o problema: testar um widget. Claro, é possível testá-lo com integration test, mas “unit-tests”, quando envolvem Javascript+Rails, envolvem HTML Fixtures, linguagens de teste diferentes, enfim, nada produtivo.

Aí, entrou a gem Johnson. Basicamente, é um interpretador Javascript dentro de Ruby, de forma que seja possível rodar código JS dentro do Ruby (e o resultado vem como uma Ruby String, ou um Numeric, enfim). Já falei sobre isso quando estudei “The Ruby Racer” e os testes com V8 no Ruby, até me interessei pelo “env.js”, porém na época a versão que rodava com Johson estava muito ruim ainda (e infelizmente, ainda está)

Porém, o novo Env.JS (1.3) já roda no Johnson. E está muito melhor.

Enfim, o env.js, porém, nessa versão virou mais um “browser” do que uma “biblioteca”, então acabei fazendo umas adaptações para ele rodar como biblioteca do Johnson. Junto com isso, fiz um pequeno hack:

document.oldGetElementById = document.getElementById;
document.getElementById = function(id) {
    var element = document.oldGetElementById(id);
    if(element) return element;
    element = document.oldGetElementById('someDivThatWillNeverBeOverwrited');
    var newDiv = document.createElement('div');
    newDiv.id = id;
    newDiv.innerHTML = "";
    element.appendChild(newDiv)
    return newDiv;
}

A idéia, aqui, é ao invés do “getElementById” retornar “undefined” se o elemento não for encontrado, criar um novo elemento sempre. Isso é importante porque, criando sempre um novo elemento, é possível testar o comportamento de um javascript, sem necessitar de “fixtures”. Esse foi o primeiro passo, basicamente. O segundo, foi implementar uma classe para abstrair todas as alterações na página, rodar o Javascript, identificar eventos e ligar com o Env.JS. Como estou usando Johnson, chamei essa classe de EnvJohnson.

Enfim, a idéia principal é: criar um ambiente Javascript, re-escrever o “getElementById” do “document”, salvar a página como estava antes e rodar um script que veio do Rails nele, salvando a página que veio depois. Isso traz um sumário de tudo o que foi alterado na página, como por exemplo, elemento que foi substituído, elemento que foi alterado, elemento que mudou a cor, mudou as propriedades tipo “style”, “class”, etc. A API que achei, por hora, é assim:

js_for response.body, :prototype do |js|
  js.should replace_inner_html_of(:example)
  js.should_not replace_inner_html_of(:example2)
end

A primeira linha inclui o “prototype”, e cria um “EnvJohnson” para o que vier de resultado no “response.body”. A partir daí, o resultado é computado e pode-se usar os modelos tipo “replace_inner_html_of” e outros que ainda vão surgir.

Claro, a biblioteca ainda está bem no começo, mas confio que muitas alterações serão feitas para ser uma abordagem possível para testes unitários bem menos “dolorosos” no Rails. Como sempre, o código está no github. Ele não funciona muito bem com prototype (problemas no Env.JS) mas JQuery roda bem, pelo menos o que eu testei até agora.

Aguardo pull requests :)

Advertisements
This entry was posted in Javascript, Ruby and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s