Entrou no ar hoje a tarde o novo site de Passione, a nova novela das oito da Rede Globo. Mas por trás deste site está muito mais que um site. É o ínicio de uma nova experiência de consumo da dramaturgia da Rede Globo na internet.
Para quem não sabe, depois de quatro anos na equipe de videos da globo.com, me engajei logo no inicio deste projeto no time dedicado a ele. Apesar de ser a mesma empresa, é um mundo totalmente novo. Novas tecnologias, novas formas de gestão, novas pessoas. Tudo novo. Foi e está sendo muito bom.
E agora, com a entrega no prazo, e o feedback de dezenas de pessoas no twitter elogiando nosso trabalho, só tenho a agradecer ao meu time que me acolheu tão bem, e a todos as equipes que nos ajudaram, seja nos dando suporte, seja nos dando dicas, seja nos incentivando. Sem vocês o resultado não seria igual. Parabéns a todos nós!
segunda-feira, 17 de maio de 2010
quarta-feira, 3 de março de 2010
Objetos fake em diversas linguagens para divertir minha vó
Quase todas as linguagens que trabalhei possuem ferramentas para criar objetos fakes e assim auxiliar na construção de testes. Mas e se essas ferramentas não existissem? Estaríamos perdidos? Claro que não! Mocks e stubs podem ser criados de diversas formas diferentes nas diversas linguagens.
Esse é um ótimo assunto pra se discutir em um jantar de família. Quer coisa mais divertida que explicar pra sua avó como construir objetos fake em diversas linguagens? Por exemplo um simples stub que conta quantas vezes um método que notifica visualização de um filme em um serviço externo é chamado. Algo similar ao que o código abaixo faz. Diversão garantida!
Em Ruby
Em ruby podemos simplesmente criar uma nova classe em tempo de execução ou adicionar um método a um objeto qualquer. É a maneira mais simples de garantir um sorrisão da sua vó tamanha facilidade. No caso do exemplo abaixo resolvi adicionar métodos a um objeto qualquer e ao final executo o teste utilizando o rspec.
Em Python
Em Python é possivel alterar métodos de instancias em tempo de execução, mas não é possivel adicionar métodos a um objeto da classe object. Neste momento minha vozinha fica decepcionada. Para fazer algo semelhante ao que fizemos em ruby teríamos então que instanciar um objeto da classe original e só depois modificar o método. Ficaria mais ou menos assim:
Esta opção passa a ser ruim quando o construtor da classe original executa algumas tarefas que são custosas para o nosso teste. Por isso, acho que em python o melhor para o este caso é criar uma classe em tempo de execução, deixando minha vó um pouco mais alegre, conforme exemplo abaixo:
Em Java
Uma forma "simples" de fazer em Java é criando uma nova classe para herdar a original e sobrescrever somente o método desejado. Mas aí cairíamos no mesmo problema que discutimos sobre um possível construtor com código muito custoso na superclasse. E minha vó, que apesar de repetir muitas vezes as mesmas histórias, não gosta de ouvir as nossas repetidas.
Uma alternativa que temos é extrair uma interface da classe original para que em nosso teste a gente possa implementar essa interface da maneira que quisermos. Ficaria mais ou menos assim:
Então em nosso teste a gente cria uma classe implementando a interface recém criada:
E depois, mesmo que neste ponto minha vó já esteje dormindo, a gente utiliza a classe fake criada no teste:
Parece que minha avó não gostou da história. Ela começou tão dinâmica e foi ficando cadas vez mais devagar. Sem dúvida eu deveria ter contato ao contrário.
Esse é um ótimo assunto pra se discutir em um jantar de família. Quer coisa mais divertida que explicar pra sua avó como construir objetos fake em diversas linguagens? Por exemplo um simples stub que conta quantas vezes um método que notifica visualização de um filme em um serviço externo é chamado. Algo similar ao que o código abaixo faz. Diversão garantida!
servico.should_receive(:notificar_visualizacao!).with(filme).once
Em Ruby
Em ruby podemos simplesmente criar uma nova classe em tempo de execução ou adicionar um método a um objeto qualquer. É a maneira mais simples de garantir um sorrisão da sua vó tamanha facilidade. No caso do exemplo abaixo resolvi adicionar métodos a um objeto qualquer e ao final executo o teste utilizando o rspec.
servico_fake = Object.new
servico_fake.instance_eval do
def notificar_visualizacao!(filme)
@filme_visualizado = filme
@quantidade_visualizacoes = quantidade_visualizacoes + 1
end
def filme_visualizado
@filme_visualizado
end
def quantidade_visualizacoes
@quantidade or 0
end
end
filme = Filme.new
filme.servico = servico_fake
filme.visualizar!
servico_fake.filme.should be_equal(filme)
servico_fake.quantidade.should == 1
Em Python
Em Python é possivel alterar métodos de instancias em tempo de execução, mas não é possivel adicionar métodos a um objeto da classe object. Neste momento minha vozinha fica decepcionada. Para fazer algo semelhante ao que fizemos em ruby teríamos então que instanciar um objeto da classe original e só depois modificar o método. Ficaria mais ou menos assim:
filme_visualizado = None
quantidade_visualizacoes = 0
def notificar_visualizacao_fake(filme):
global filme_visualizado, filme_visualizado
filme_visualizado = filme
quantidade_visualizacoes += 1
servico_fake = ServicoExterno()
servico_fake.notificar_visualizacao = notificar_visualizacao_fake
Esta opção passa a ser ruim quando o construtor da classe original executa algumas tarefas que são custosas para o nosso teste. Por isso, acho que em python o melhor para o este caso é criar uma classe em tempo de execução, deixando minha vó um pouco mais alegre, conforme exemplo abaixo:
class ServicoFake(object):
def __init__(self):
self.filme_visualizado = None
self.quantidade_visualizacoes = 0
def notificar_visualizacao(self,filme):
self.filme_visualizado = filme
self.quantidade_visualizacoes += 1
servico_fake = ServicoFake()
filme = Filme()
filme.servico = servico_fake
filme.visualizar()
assert servico_fake.filme_visualizado is filme
assert servico_fake.quantidade_visualizacoes == 1
Em Java
Uma forma "simples" de fazer em Java é criando uma nova classe para herdar a original e sobrescrever somente o método desejado. Mas aí cairíamos no mesmo problema que discutimos sobre um possível construtor com código muito custoso na superclasse. E minha vó, que apesar de repetir muitas vezes as mesmas histórias, não gosta de ouvir as nossas repetidas.
Uma alternativa que temos é extrair uma interface da classe original para que em nosso teste a gente possa implementar essa interface da maneira que quisermos. Ficaria mais ou menos assim:
//ServicoExterno.java
public interface ServicoExterno {
void notificarVisualizacao(Filme filme);
}
//ServicoExternoHTTP.java
public class ServicoExternoHTTP implements ServicoExterno {
public ServicoExternoHTTP() {
//Faz um monte de coisas
}
public void notificarVisualizacao(Filme filme) {
//Faz mais coisas ainda
}
}
Então em nosso teste a gente cria uma classe implementando a interface recém criada:
public class ServicoExternoFake implements ServicoExterno {
public int quantidade_visualizacoes = 0;
public Filme filme_visualizado = null;
public void notificarVisualizacao(Filme filme) {
filme_visualizado = null;
quantidade_visualizacoes++;
}
}
E depois, mesmo que neste ponto minha vó já esteje dormindo, a gente utiliza a classe fake criada no teste:
ServicoExternoFake servicoFake = new ServicoExternoFake();
Filme filme = new Filme();
filme.setServico(servicoFake);
filme.visualizar();
assertSame(filme,servicoFake.filme_visualizado);
assertEquals(1,servicoFake.quantidade_visualizacoes);
Parece que minha avó não gostou da história. Ela começou tão dinâmica e foi ficando cadas vez mais devagar. Sem dúvida eu deveria ter contato ao contrário.
terça-feira, 9 de fevereiro de 2010
Upload de arquivo com selenium server no firefox
Para fazer upload de arquivo com selenium é preciso alterar seu selenium-server.jar liberando permissão para que seja possível manipular campos do tipo file por javascript. Para fazer isso, extraia o selenium-server.jar:
Crie um arquivo chamado user.js no diretório customProfileDirCUSTFF contendo um código semelhante com o exibido abaixo:
Repare que a liberação de acesso é feita por host. No caso do exemplo estou liberando apenas para o ambiente local. Se desejar liberar outros hosts baixa adicionar outros conforme o exemplo abaixo:
Feito isso, basta gerar novamente o jar e executá-lo.
Já será possivel preencher o path de qualquer arquivo no campo como se ele fosse apenas um campo de textos. Isso no Firefox é claro.
jar -xvf selenium-server.jar
Crie um arquivo chamado user.js no diretório customProfileDirCUSTFF contendo um código semelhante com o exibido abaixo:
user_pref("signed.applets.codebase_principal_support", true);
user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p0.id", "http://localhost");
user_pref("capability.principal.codebase.p0.subjectName", "");Repare que a liberação de acesso é feita por host. No caso do exemplo estou liberando apenas para o ambiente local. Se desejar liberar outros hosts baixa adicionar outros conforme o exemplo abaixo:
user_pref("signed.applets.codebase_principal_support", true);
user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p0.id", "http://localhost");
user_pref("capability.principal.codebase.p0.subjectName", "");
user_pref("capability.principal.codebase.p1.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p1.id", "http://globo.com");
user_pref("capability.principal.codebase.p1.subjectName", "");Feito isso, basta gerar novamente o jar e executá-lo.
jar cvfm selenium-server.jar ./META-INF/MANIFEST.MF -C ./ .
java -jar selenium-server.jar
Já será possivel preencher o path de qualquer arquivo no campo como se ele fosse apenas um campo de textos. Isso no Firefox é claro.
sexta-feira, 15 de janeiro de 2010
Diferenças entre ruby e python: Que perigo
Em ruby:
Em python:
def a(z=[])
z.push 'a'
end
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]
Em python:
def a(z=[]):
z.append('a')
return z
a() # retorna ['a']
a() # retorna ['a','a']
a() # retorna ['a','a','a']
a() # retorna ['a','a','a','a']
quarta-feira, 25 de novembro de 2009
Utilizando asserts para testar layouts
Um dos problemas encontrados em nossos testes de aceitação é a impossibilidade de validar a aparência exata do resultado final de uma determinada ação do usuário. Conseguimos validar com o watir se determinada div possui um texto, se determinado link está presente, mas nada impede que eles estejam escondidos, por trás de outro div, ou com letras da mesma cor do fundo. É sempre útil, mas não necessáriamente exato, sendo sempre um ponto de falha. Ainda mais em um sistema como o que estamos trabalhando em que o visual para o usuário tem uma grande importância.
Eu e o quixadá, mestre do javascript e meu par de hoje, trabalhamos em uma correção de bug visual, e como costumamos trabalhar com desenvolvimento outside-in, chegamos ao dilema de como criar um teste para garantir que a falha existia. A solução encontrada foi inserir asserts dentro do código javascript, tal qual a funcionalidade de asserts do java. O código da função assert ficou parecido com o mostrado abaixo:
Em nosso caso, tinhamos que ter a certeza de que após um clique do usuário a barra de rolagem do elemento mantinha-se da mesma forma que anteriormente. Então o código ficou parecido com o mostrado abaixo:
No teste de aceitação verificamos então que o texto 'Deveria manter scroll igual' não deveria aparecer. O código do step do cucumber utilizando o watir pode ser visto abaixo:
Com o teste pronto e falhando, aí sim corrigimos a função javascript substituiOpcoes(html) de forma a manter o scroll anterior.
Não é uma solução perfeita. Estamos pesquisando uma melhor, como pode ser vista no post Testes de aceitação automáticos para Flash com T-Plan Robot do Anselmo. Mas enquanto isso podemos evitar alguns pontos de falha visuais que costumávamos ter que testar manualmente. Dado que nosso sistema possui 100% de cobertura de testes unitários, somados a 152 cenários de teste de aceitação que abragem 1568 passos, acho que estamos indo por um bom caminho.
Eu e o quixadá, mestre do javascript e meu par de hoje, trabalhamos em uma correção de bug visual, e como costumamos trabalhar com desenvolvimento outside-in, chegamos ao dilema de como criar um teste para garantir que a falha existia. A solução encontrada foi inserir asserts dentro do código javascript, tal qual a funcionalidade de asserts do java. O código da função assert ficou parecido com o mostrado abaixo:
function assert(mensagem,valorDesejado,valorRecebido) {
if( valorDesejado != valorRecebido ) {
var html = '<div class="warning">' + mensagem + '</div>';
$('body').append(html);
throw mensagem;
}
}
Em nosso caso, tinhamos que ter a certeza de que após um clique do usuário a barra de rolagem do elemento mantinha-se da mesma forma que anteriormente. Então o código ficou parecido com o mostrado abaixo:
$.get( url, function ( responseHtml ) {
var scrollAnterior = $('#opcoes').scrollTop();
substituiOpcoes( responseHtml );
assert( 'Deveria manter scroll igual', scrollAnterior, $('#opcoes').scrollTop() );
});
No teste de aceitação verificamos então que o texto 'Deveria manter scroll igual' não deveria aparecer. O código do step do cucumber utilizando o watir pode ser visto abaixo:
Then /^a barra de rolagem deveria permanecer na mesma posição$/ do
@browser.text.should_not include('Deveria manter scroll igual')
end
Com o teste pronto e falhando, aí sim corrigimos a função javascript substituiOpcoes(html) de forma a manter o scroll anterior.
Não é uma solução perfeita. Estamos pesquisando uma melhor, como pode ser vista no post Testes de aceitação automáticos para Flash com T-Plan Robot do Anselmo. Mas enquanto isso podemos evitar alguns pontos de falha visuais que costumávamos ter que testar manualmente. Dado que nosso sistema possui 100% de cobertura de testes unitários, somados a 152 cenários de teste de aceitação que abragem 1568 passos, acho que estamos indo por um bom caminho.
sexta-feira, 30 de outubro de 2009
API rest para OpenSocial do Orkut com ruby
A documentação da API rest do OpenSocial do Orkut detalha muito bem as opções e formatos de retorno disponíveis porém é um tanto vaga sobre como fazer a autenticação necessária para usá-la. Basicamente lá é explicado os parâmetros a serem enviados e que o protocolo é o OAuth. Então detalho aqui como obter por exemplo os dados de um usuário apartir desta API.
Em primeiro lugar é preciso obter a consumer key e consumer secret de sua aplicação. Isso é feito gerando um token aqui: https://www.google.com/gadgets/directory/verify. Você deve colocar esse token dentro da tag content do xml descritor de sua aplicação e depois fazer a validação provando que é dono da aplicação. Com isso o Google irá lhe informar seu consumer key e consumer secret. Guarde eles com carinho.
Depois, com a gem oauth instalada você deverá executar um código semelhante ao exibido abaixo, com a premissa de que as variaveis consumer_key e consumer_secret estão preenchidas com os correspondentes à sua aplicação. E que a variável id é o id do usuário do Orkut que você está querendo conhecer melhor.
Em primeiro lugar é preciso obter a consumer key e consumer secret de sua aplicação. Isso é feito gerando um token aqui: https://www.google.com/gadgets/directory/verify. Você deve colocar esse token dentro da tag content do xml descritor de sua aplicação e depois fazer a validação provando que é dono da aplicação. Com isso o Google irá lhe informar seu consumer key e consumer secret. Guarde eles com carinho.
Depois, com a gem oauth instalada você deverá executar um código semelhante ao exibido abaixo, com a premissa de que as variaveis consumer_key e consumer_secret estão preenchidas com os correspondentes à sua aplicação. E que a variável id é o id do usuário do Orkut que você está querendo conhecer melhor.
consumer = OAuth::Consumer.new(
consumer_key,
consumer_secret,
:site => 'http://www.orkut.com',
:scheme => :query_string,
:http_method => :get
)
request = consumer.create_signed_request(:get,
"/social/rest/people/#{id}/@self?xoauth_requestor_id=#{id}")
res = Net::HTTP.start('www.orkut.com', 80) do
|h| h.request(request)
end
puts res.body
sábado, 1 de agosto de 2009
TralhaController notificando obervadores por pattern
TralhaController é uma biblioteca javascript que permite que aplicações web que utilizam requisições assíncronas ofereçam URLs para cada contexto de navegação do usuário. Para saber mais sobre ela basta ler o artigo Mantendo contexto usando ajax que o Bruno Carvalho escreveu recentemente.
Porém uma coisa me incomodava no uso dessa classe. Para cada observador registrado eu precisava dentro do método update verificar se ele precisava agir de acordo com a URL. Isso acaba por tornar o código mais confuso pois obrigava-nos a misturar validações e lógica de negócio. Veja um exemplo abaixo:
Para tentar melhorar isso, pus a mão na massa e alterei o TralhaController permitindo registrar observadores que só fossem notificados diante de URLs que tivessem o padrão desejado. Utilizando então a última versão da biblioteca o código acima poderia então ser alterado para ficar da seguinte forma:
Bem mais simples certo? É claro que há aqueles que não são muito fãs de expressões regulares e vão achar a primeira forma melhor. Para esses não há problema pois a forma antiga continua funcionando. Observadores registrados sem padrões continuam sendo notificados a toda e qualquer modificação de URL.
Porém uma coisa me incomodava no uso dessa classe. Para cada observador registrado eu precisava dentro do método update verificar se ele precisava agir de acordo com a URL. Isso acaba por tornar o código mais confuso pois obrigava-nos a misturar validações e lógica de negócio. Veja um exemplo abaixo:
var observadorBusca = { update:function(url) {
if( url.indexOf("#busca=")!=-1 ) {
//Executa a busca
}
} };
var observadorLink = { update:function(url) {
if( url.indexOf("#link=")!=-1 ) {
//Executa o link clicado
}
} };
TralhaController.addObserver(observadorBusca);
TralhaController.addObserver(observadorLink);
Para tentar melhorar isso, pus a mão na massa e alterei o TralhaController permitindo registrar observadores que só fossem notificados diante de URLs que tivessem o padrão desejado. Utilizando então a última versão da biblioteca o código acima poderia então ser alterado para ficar da seguinte forma:
var observadorBusca = { update:function(url) { /* Executa a busca */ } };
var observadorLink = { update:function(url) { /* Executa o link clicado */ } };
TralhaController.addObserver("\\#busca\=.*", observadorBusca);
TralhaController.addObserver("\\#link\=.*", observadorLink);
Bem mais simples certo? É claro que há aqueles que não são muito fãs de expressões regulares e vão achar a primeira forma melhor. Para esses não há problema pois a forma antiga continua funcionando. Observadores registrados sem padrões continuam sendo notificados a toda e qualquer modificação de URL.
Assinar:
Postagens (Atom)