11 November 2014 / ruby
Quando comecei a automatizar testes, eu morria de medo quando olhava em alguns tutoriais de desenvolvimento na internet e aparecia alguns exemplos com strings estranhas que não faziam sentido nenhum na época como por exemplo.. /\d{2}\w$/
Só depois fui descobrir que esse ideogramas egípcios eram na verdade strings regex.
Assim como a sintaxe padrão SQL muda de acordo com a aplicação de banco de dados (o jeito que você escreve para oracleDB é um pouco diferente do que você escreve para mySQL, MS-SQL, etc), regex é a mesma coisa.
A base do regex é a mesma, e mesmo com algumas diferenças de linguagem para linguagem, você pode usar a base tanto no seu código base quanto no seu editor de texto para fazer alguma busca (notepad++ e sublime entendem regex <3).
Como a moda agora é cucumber com capybara, vou focar patterns que funcionam bem em ruby.
Regex é o diminutivo para regular expression e você pode ler a definição técnica no wikipédia, ou caso você esteja com preguiça, segue aqui minha definição TL:DR: "Expressão regular é uma forma de identificar cadeias de caracteres dentro de uma string". É muito mais do que isso, mas para o momento, acho que é suficiente.
Como falei, regex é usado para fazer buscas de padrões (patterns)dentro de uma string, ou seja, pode ser muito útil em sua automação.
Digamos que você precise buscar algum token, senha ou até email na tela, que pode ou não fica meio jogado em algum lugar.
Você não sabe o que será gerado, mas sabe que emails começam com algumvalor@dominio.com ou que telefones exibidos na tela devem respeitar o padrão (nn) nnnnn-nnnn ou até que senhas geradas devem conter somente números, ou caracteres maiúsculos com números.
Se você abrir o código fonte da aplicação que você testa, provavelmente você verá diversas validações usando regex.
Eu sei que é difícil, que entender regex de outras pessoas é bem complicado, mas isso pode ajudar você em algumas tarefas do dia a dia.
TL:DR, assista esse vídeo da Lea Verou (w3c) que fala sobre tema de uma forma bem humorada :) e clara ( e com um aplicativo maroto para testar suas skills novas http://leaverou.github.com/regexplained )
escrevendo seu primeiro regex
/keeptesting.com.br/
O que define o começo e o fim de um string regex é a barra / ( essa / é diferente dessa \ que já falo para o que serve)
Dentro dos limites das duas barras / fica o modelo(pattern) do que você está buscando.
Tudo é questão de como você vai compor esse pattern.
O exemplo acima /keeptesting.com.br/ é bem simples, vai procurar a string keeptesting.com.br mas para isso não precisaríamos de regex né?
Eu poderia também procurar pelo seguinte pattern:
Isso irá retornar sempre que achar a palavra testing que por sinal também se encontra na string keeptesting.com.br
Abrangendo mais o campo de pesquisa, toda vez que g for encontrado, será retornado por esse regex.
ué, mas isso não é regex... isso é uma busca normal.. nada de novo
Quando fazemos busca em strings grandes, até o regex mais simples pode ajudar.
E sabe o que mais? pegadinha do malandro..
Sabia que /keeptesting.com.br/ pode retornar coisas como keeptestingacom$br? ou até /keeptesting0com!br/
O caracter . (ponto) é como se fosse um coringa, onde você define que ali pode ter qualquer coisa (menos quebra de linha), por isso muito cuidado ao utilizar pois pode trazer falso positivo.
Poderíamos também procurar por essa padrão no começo das strings ou no final delas
/^keeptesting\.com\.br/
/keeptesting\.com\.br$/
Na procura pelo padrão keeptesting.com.br, quando usado o caracter ^ eu defino que se esse padrão não estiver no começo das strings buscadas, não é retornado nada.
O mesmo vale para o caracter $ informando que se esse padrão não for achado no final da string, não é retornado nada.
Espera um pouco... eu lembro disso nos meus steps de cucumber!
Congrats, se você não sabia que seus steps do cucumber usavam regex para que o arquivo de feature pudesse fazer alguma relação com os arquivos de steps, agora você já sabe.
mas o que é a \ que você colocou?
Neste caso, a barra invertida foi utilizada para informar que aquele ponto é para ser usado como ponto e não como caracter especial (metacharacter)(famoso escape da linguagem)
tld:dr2 veja a listagem de todos os comandos regex em ruby com uma pequena descrição aqui --> http://www.tutorialspoint.com/ruby/ruby_regular_expressions.htm
Digamos que seu teste precise verificar que o telefone foi cadastrado com sucesso e está nos padrões que foi definido no critério de aceitação
Vamos definir um exemplo de número válido:
11-102323571
Como podem ver, o modelo é composto de 2 números (11), um caracter especial (-) e mais 9 números (102323571)
Vamos cobrir 2 conceitos de uma vez:
1 - Precisamos saber como procurar somente números
2 - Precisamos definir o número de vezes que esse padrão se repete.
O código acima \d serve para informar que estou buscando um número de 0 a 9.
O mesmo poderia ser feito desta maneira:
Geralmente quando estamos aprendendo a fazer regex, esse formado é muito mais facil de ler, porém pode aumentar consideravelmente seu regex, complicando mais do que ajudando.
Agora precisamos informar que estamos procurando 2 números para validar se o ddd está presente:
/\d{2}/
Não precisa explicara mais nada né?
/\d{2}-\d{9}/
Agora para melhorar um pouco o seu teste, existe DDD's brasileiros que ainda não tem nono digito, então o regex acima iria falhar.
Para resolver esse problema, podemos usar um mini truque dizendo que a gente espera no mínimo 8 dígitos e no máximo 9 dígitos:
/\d{,3}-\d{8,9}/
Aproveitei e coloquei também para DDD pois alguns podem digitar o DDD como 011 ou somente 11.
Agora digamos que você precise validar algum formato especifico, por exemplo, um email na tela.
leo@keeptesting.com.br
Assim como usado o [0-9] para buscar por números, podemos usar a seguinte sintaxe para buscar qualquer letra:
No exemplo do email, podemos fazer o seguinte:
/\w{1,20}@\w{3,20}\.\w{2,6}.{0,1}\w{0,2}/
Não se assuste porque você já entendeu ( e eu compliquei um pouco de propósito :D )
\w{1,20} : define que o modelo deve ter no mínimo 1 e no máximo 20 letras
@ : todo email precisa ter esse caracter!
\w{3,20} : define que o modelo deve ter no mínimo 3 (padrão de internet, domínios não podem ter menos que 3 caracteres) e no máximo 20 letras.
\. : informando que é necessário o caracter ponto
\w{2,6} : domínios de topo por país podem começar com 2 caracteres (por exemplo .tv) e geralmente chegam até 6 caracteres (por exemplo .museum)(tecnicamente existe a possibilidade de ter domínios de topo com mais de 6 caracteres...)
.{0,1}\w{0,2} : o domínio pode ser genérico ou específico por região, logo esse campo não é obrigatório (primeiro usado o caracter . para definir que pode ou não existir esse caracter)
Da para melhorar? Lógico!
Mesmo por que tem algumas falhas nesse regex (caso o email da pessoa tenha . (ponto) , _ (underline) ou -(hífen)) e existem domínios com hífen e underline, que não está coberto.
/[\w-_.]+@[\w-.]+\.\w+([-.]\w{0,2})/
[\w-_.]+ : Agora estou dizendo que é possivel ter qualquer caracter que também contenha os caracteres underline, ponto e hífen.
Facil né? :D
Ou para ficar um pouco mais enxuto e bonito e com menos falso positivos do que o anterior:
/[\w-_.]+@[\w-]+(\.\w{2,3}){1,2}/
(\.\w{2,3}){1,2} : estou agrupando esse pattern \.\w{2,3} e depois informo que ele pode aparecer 1 ou 2 vezes
mas como eu uso isso nos meus scripts?
regex = /[\w-_.]+@[\w-]+(\.\w{2,3}){1,2}/
email = page.find(:css, "div.class.selector.email").text
email =~ regex ? "true" : "false"
Não é necessário colocar nenhum tipo de aspas.. and you are good to go!
Como eu já falei, agora é só dar uma olhada na listagem de argumentos que você pode usar (http://www.tutorialspoint.com/ruby/ruby_regular_expressions.htm) e ver se funcionam aqui (http://leaverou.github.io/regexplained/)
PS: A propósito, você sabe qual regex seria o ideal para dar match em qualquer email valido? acessa aqui --> http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html