For foreing raders, please clique here to read this post in english (google translator)
Não deixe de ler também a
Integração do Selenium e Testlink.
Escopo
Este tutorial vai apresentar como efetuar a integração entre o
Selenium RC e o
Mantis, para que seja possível reportar automaticamente um bug quando um erro em um script do Selenium falhar.
Criaremos um projeto em Java com o suporte do
JUnit para a criação do script de teste com o
Selenium. abaixo há uma lista de itens necessários para esse tutorial.
O que é necessário?
- Mantis versão 1.2.x ou superior
- Neste tutorial estou usando a versão 1.2.3
- MantisConnect
- IDE de desenvolvimento em Java
- Java JDK instalada
- Neste tutorial estou usando a JDK 1.6.0_17, porém qualquer uma a partir da 5.0 serve
- Bibliotecas do Selenium RC para Java
- Baixar a versão disponível no site: 1.0.3
2. Preparando seu ambiente de Desenvolvimento
A preparação do ambiente vai apresentar somente as bibliotecas necessárias para fazer a integração funcionar. Não é intuito deste item ensinar alguma coisa sobre o Eclipse ou sobre Java.
2.1 Selecionando as bibliotecas necessárias
Para rodar o script de teste com a integração e necessário os seguintes arquivos (bibliotecas e libraries) das seguintes API's / frameworks:
- mantisconnect: necessário adicionar as seguintes bibliotecas
- mantisconnect-client-api-1.1.1.0.jar
- todas as libs, exeto a junit, da pasta lib
- Selenium RC: necessário adicionar as seguintes bibliotecas
- selenium-server.jar
- selenium-java-client-driver.jar
- JUnit: necessário adicionar o JUnit 4 que já vem com o Eclipse (Add Libraries)
Abaixo segue a imagem das bibliotecas que foram adicionadas ao Eclipse.
Para adicionar cada biblioteca você precisa clicar com o botão direito no nome do projeto e selecionar
Build Path/Configure Build Path...
Adicone também a biblioteca do JUnit, indo ao mesmo local (
Configure Build Path...) e clicando no botão
Add Library. Selecione JUnit e, em seguida, JUnit 4.
2.1 Desenvolvendo o código-fonte
A aplicação de exemplo, que está no final do post traz o código-fonte do script de teste e mais dois arquivos de código-fonte, que serão explicados abaixo.
A primeira coisa a fazer é entender o script de teste primeiro. O package, os imports e comentários em javadoc foram excluídos para exemplificar o funcionamento da classe.
Existem duas classes de teste, uma com execução OK e outra que forçará o erro para que seja possível o report automático do bug.
Abaixo será apresentado o script que força o erro.
1: public class CasoTesteMantisNOK extends TestCase implements IConstantes {
2:
3: Selenium selenium;
4: SeleniumServer server;
5:
6: String serverHost = "localhost";
7: int serverPort = 4444;
8: String browserStartCommand = "*firefox";
9: String browserURL = "http://www.lojaexemplodelivros.com.br/";
10:
11: boolean erro;
12: String msgErro;
13: String evidenciaErro;
14:
15: public void setUp() throws Exception {
16: selenium = new DefaultSelenium(serverHost, serverPort, browserStartCommand, browserURL);
17: server = new SeleniumServer();
18:
19: server.start();
20: selenium.start();
21: }
22:
23: @Test
24: public void testPesquisaLivro() throws Exception {
25:
26: try {
27: selenium.open("/");
28: selenium.click("//ul[@id='nav']/li[1]/ul/li[2]/ul/li[1]/a/span");
29: selenium.waitForPageToLoad("30000");
30:
31: assertEquals("3 Item(s)", selenium.getText("//div[@id='main']/table[1]/tbody/tr/td[1]/strong"));
32: assertEquals("[PRODUTO] - Use a Cabeça! Java", selenium.getText("link=[PRODUTO DE EXEMPLO] - Use a Cabeça! Java"));
33: assertEquals("[PRODUTO DE EXEMPLO] - Entendendo e Dominando o Java: para Internet", selenium.getText("link=[PRODUTO DE EXEMPLO] - Entendendo e Dominando o Java: para Internet"));
34: assertEquals("[PRODUTO DE EXEMPLO] - Ajax com Java", selenium.getText("link=[PRODUTO DE EXEMPLO] - Ajax com Java"));
35:
36: selenium.click("//img[@alt='[PRODUTO DE EXEMPLO] - Ajax com Java']");
37: selenium.waitForPageToLoad("30000");
38: assertTrue(selenium.isTextPresent("2x R$ 222,25 sem juros"));
39: assertTrue(selenium.isTextPresent("3x R$ 148,17 sem juros"));
40: assertTrue(selenium.isTextPresent("4x R$ 111,13 sem juros"));
41: assertTrue(selenium.isTextPresent("5x R$ 88,90 sem juros"));
42:
43: } catch (AssertionError e) {
44: reportError(e);
45:
46: } catch (Exception e) {
47: reportError(e);
48:
49: } finally {
50: if (erro) {
51: MantisReport.reporIssue("Erro no Caso de Teste de Pesquisa de Livros", "Erro em alguma validacao ou validacao", "General", msgErro, evidenciaErro, "CasoTesteMantisNOK");
52: CasoTesteMantisNOK.fail(msgErro);
53: }
54: }
55: }
56:
57: private void reportError(Throwable e) {
58: erro = true;
59: msgErro = e.getMessage();
60: e.printStackTrace();
61: evidenciaErro = selenium.captureEntirePageScreenshotToString("background=#FFFFFF");
62: }
63:
64: public void tearDown() throws Exception {
65: selenium.stop();
66: server.stop();
67: }
68: }
Basicamente há a criação dos atributos do
Selenium e
SeleniumServer para a execução, os métodos
setup() e
tearDown() do JUnit e o caso de teste automatizado.
Note que o bloco do caso de teste (método
testPesquisaLivro) está com um
try-catch. Isso é necessário para que possamos reportar um bug quando o script falhar.
Nas linhas 11, 12 e 13 foram criados atributos que controlarão os dados para o erro, e serão descritos logo mais.
A linha 32 deste script irá falhar, propositalmente, para que o bug seja reportado. O trecho de código correto (que não causará um bug) é o abaixo e também esta no arquivo
CasoTesteMantisOK:
assertEquals("[PRODUTO DE EXEMPLO] - Use a Cabeça! Java", selenium.getText("link=[PRODUTO DE EXEMPLO] - Use a Cabeça! Java"));
Existem 2 blocos com
catch (um iniciando na linha 43 e outro na linha 46). Foram colocados dois somente para distinguir o tipo do erro, se um erro na validação (
AssertionError) ou qualquer erro (
Exception). Dentro destes blocos há um método chamado
reportError passando como parâmetro a exception.
Este método (linhas 57 a 62) informa através da variável
erro que um erro ocorreu (passando
true). Pega a mensagem de erro e coloca no atributo
msgErro, faz com que a exception seja apresentada o console (
e.printStackTrace()) e captura a imagem da página utilizando a função
captureEntirePageScreenshotToString() para passar a imagem em formato de String Base64.
PS: esse comando só funcionará rodando com a angine do Mozilla (Firefox) e no Google Chrome. Caso queira que o mesmo comando funcione no IE leia
este post (em inglês).
Na linha 50 e feito uma condição para ver se ocorreu algum erro (se o atributo
erro está como
true), caso positivo a função de report do bug é chamada.
A linha 51 apresenta a função utilizada para reportar o bug, que será explicado logo mais. Neste momento você só precisa saber que é necessário informar os seguintes dados (nesta ordem) para o método:
- Sumário do bug
- Descrição do bug
- Categoria do bug
- Informação adicional do bug
- Evidencia (como String Base64)
- Nome do arquivo (que será anexado)
A linha 52 força uma falha no script mostrando a mensagem de erro ocorrida.
Classe MatisReport
Dentro do projeto no pacote
com.blogspot.sembugs há a classe
MantisReport, que é a responsável por reportar o bug no Mantis. Eu gerei essa classe como chamada de uma função da API
mantisconnect.
PS: package, imports e comentários em javadoc foram excluídos para exemplificar o funcionamento da classe.
1: public class MantisReport implements IConstantes {
2:
3: public static void reporIssue(String sumario, String descricao, String categoria, String informacaoAdicional, String evidencia, String nomeArquivo) {
4: IMCSession sessao = null;
5: String arquivo = nomeArquivo + ".png";
6:
7: try {
8: sessao = ConnectMantis.getSessao();
9: IProject projeto = sessao.getProject(PROJETO);
10:
11: Issue issue = new Issue();
12:
13: issue.setProject(new MCAttribute(projeto.getId(), projeto.getName()));
14: issue.setAdditionalInformation(null);
15: issue.setOs(System.getProperty("os.name"));
16: issue.setOsBuild(System.getProperty("os.version"));
17: issue.setPlatform(System.getProperty("os.arch"));
18: issue.setSeverity(new MCAttribute(70, "crash"));
19: issue.setReproducibility(new MCAttribute(10, "always"));
20: issue.setSummary(sumario + new Date());
21: issue.setDescription(descricao);
22: issue.setCategory(categoria);
23: issue.setPriority(new MCAttribute(40, "high"));
24: issue.setAdditionalInformation(informacaoAdicional);
25:
26: long id = sessao.addIssue(issue);
27: sessao.addIssueAttachment(id, arquivo, "image/png", Base64.decodeBase64(evidencia));
28:
29: } catch (MalformedURLException e) {
30: System.err.println("Erro na URL de acesso ao Mantis");
31: e.printStackTrace();
32: } catch (MCException e) {
33: System.err.println("Erro na comunicacao com o Mantis");
34: e.printStackTrace();
35: }
36: }
37: }
A linha 5 cria um atributo que pega o nome do arquivo, que foi passado como parâmetro, e concatena a extensão ".png", que será necessário para anexar o arquivo no Mantis.
Na linha 8 é feita uma chamada para o Singleton (que será explicado depois) para fazer a conexão com o Mantis e retornar um objeto de sessão do Mantis (
IMSession)
A linha 9 traz o projeto, que está na interface
IConstantes, como objeto de projeto do Mantis (
IProject). Isso é necessário para sabermos em qual projeto reportar o bug.
Na linha 11 é criado uma issue (bug). A classe
Issue representa um relato de bug.
A linha 13 seta o projeto que reportaremos o bug e das linhas 14 a 24 passamos diversas informações do bug para o Mantis. Existe uma série de informações que podemos passar, eu coloquei apenas as mais relevantes aqui.
Atenção: na linha 20 eu concatenei o sumário com a data atual (
issue.setSummary(sumario + new Date());). Fiz isso para que seja possível executar diversas vezes o script sem duplicar o nome do bug no Mantis.
Note que nas linhas 18, 19 e 23 é preciso criar um objeto
MCAttibute para que seja possível passar informações de qualquer atributo no report do bug como Severidade, Prioridade e Frequencia.
Serão sempre duas informações: o código e o nome do atributo.
Você pode consultar o código e nome dos atributos no arquivo
config_defaults_inc.php e consultar cada atributo.
Se você quiser visualizar ou alterar estes atributos que estão no código-fonte, dê uma olhada nos atributos abaixo que estão contidos no arquivo citado acima:
1: $g_severity_enum_string = '10:feature,20:trivial,30:text,40:tweak,50:minor,60:major,70:crash,80:block';
2:
3: $g_priority_enum_string = '10:none,20:low,30:normal,40:high,50:urgent,60:immediate';
4:
5: $g_reproducibility_enum_string = '10:always,30:sometimes,50:random,70:have not tried,90:unable to duplicate,100:N/A';
Na linha 26 o bug é submetido para o cadastro, retornando o código do bug.
A linha 27 adiciona um anexo no bug, que é a tela capturada pelo Selenium no momento do erro. Para isso é necessário passar para a função
addIssueAttachment da sessão (e não da issue) os parâmetros: id do bug, nome do arquivo, tipo do arquivo e o array de
bytes do arquivo (por isso a transformação em
Base64, que é como o Selenium retorna a imagem).
Essa classe foi criada para facilitar o report do bug, nada impede de criarmos outras funções com mais informações ou simplesmente colocar esse código todo no script. Isso foi feito pensando em uma maior reutilização de código... ;)
Classe ConnectMantis (Singleton)
Para que seja possível reportar o bug é necessário efetuar a conexão com o Mantis. A classe criada utiliza o Design Pattern Singleton, para que não exista várias instâncias de conexão com o Mantis, mantendo apenas uma ativa em toda a execução da aplicação.
Não é o foco explicar como funciona o Design Pattern Singleton, mas você pode clicar nos links abaixo para aprender um pouco. Vou me ater apenas a um trecho do código desta classe.
Design Pattern:
http://en.wikipedia.org/wiki/Design_pattern
Singleton:
http://en.wikipedia.org/wiki/Singleton_pattern
1: public ConnectMantis() throws MalformedURLException, MCException {
2: URL url = new URL(MANTIS_URL);
3: sessao = new MCSession(url, MANTIS_USER, MANTIS_PWD);
4: }
Na função acima, que está contida na classe é criado um objeto
URL com a URL do Mantis que está na Interface
IConstantes e na linha 3 é criado um novo objeto do tipo sessão (
IMSession) passando a URL, usuário e senha do Mantis.
Interface IConstantes
A interface contém apenas constantes utilizadas em comum pela aplicação, e também para ter um ponto único de alterações de usuário e senha quando existir.
1: public interface IConstantes {
2: static final String MANTIS_URL = "http://localhost/mantisbt-1.2.3/api/soap/mantisconnect.php";
3: static final String MANTIS_USER = "administrator";
4: static final String MANTIS_PWD = "root";
5: static final String PROJETO = "Integracao";
6: }
Na linha 2 a contante é a URL de acesso aos serviços SOAP do Mantis. Tome cuidado quando você for alterar essa constante, pois você terá que colocar o seu servidor (onde aqui está como
localhost) e o nome de acesso a aplicação (aqui está como
mantisbt-1.2.3)
As outras contantes são o usuário na linha 3, a senha na linha 4 e o nome do projeto na linha 5.
4. Execução e modificação deste tutorial
Chegamos ao fim do tutorial. Se você deseja executar este tutorial baixe os fontes de exemplo criados no Eclipse e altere os dados de URL para o Mantis.
Se você alterar os dados da interface
IConstantes o exemplo não funcionará, mas você pode usar o tutorial como base para a sua integração.
Por favor mandem sugestões e feedback's se este tutorial tem ajudado ou mesmo se estiver difícil de entender (claro que dentro de seus conhecimentos de programação Java).
4.1 Para entender mais
Você pode fazer a integração com outras linguagens de programação com o Mantis , não especificamente com o Selenium. Na verdade essa é uma implementação em Java para qualquer aplicação desenvolvida em Java, não específica para o Selenium. O que fiz foi inserir dentro do código do Selenium a integração!
Para saber mais da API Java utilizada para a comunicação com o Mantis acesse:
4.2 Fontes deste tutorial
Arquivo zipado contendo o projeto desenvolvido no Eclipse:
http://www.eliasnogueira.com/arquivos_blog/selenium/integracao/mantis/mantis-selenium-example.zip
Em breve sai o projeto com a integração em conjunto do Mantis e do Testlink!
Abraço a todos!