Automação de testes para Mainframe: Utilizando a biblioteca f3270
Utilizando uma biblioteca open-source para a automação de testes para terminais 3270 e 5250.
Dando continuidade ao artigo publicado a respeito de ambientes Mainframe para prova de conceito, será explicado com mais detalhes a utilização do f3270, uma biblioteca open-source que permite a automação de testes para terminais 3270 e 5250.
O que é o f3270?
De acordo com o descrito no README do repositório r5v9/f3270:
f3270 é um framework Java simples e fácil de usar para automatizar testes funcionais de mainframe. Consiste em uma API para lidar com campos em telas, e um terminal GUI para depuração.
O repositório original do projeto não fornece demais informações de como utilizar a biblioteca, porém avaliando a estrutura do projeto, trata-se de um projeto Java com a gestão de build and deploy ficando a cargo do Apache Ant.
Outro aspecto desta biblioteca, é que a mesma utiliza o s3270, uma ferramenta de screen scrapping, emulando comandos do terminal por instruções enviadas via prompt de comando.
Screen scrapping é o processo de acessar dados em um mainframe por meio de um programa que controla o comportamento de um emulador de terminal. Ou seja, o mainframe acredita que está interagindo com um usuário humano operando um terminal, mas o usuário na verdade é um programa. O programa envia dados para o Mainframe digitado em um teclado virtual e lê os resultados em uma tela virtual.
Fazendo um comparativo com automação Web, o s3270 poderia ser comparado a um ChromeDriver ou GeckoDriver, e o f3270 seria comparado a um Selenium, aonde é possível a execução de comandos que simulam as ações de um usuário.
Ficaria muito mais simples a configuração de um projeto com uma ferramenta de gestão de dependências, como Maven ou Gradle. Por sorte, um dos forks do projeto original, o vebqa/f3270, já possui esta configuração, tendo inclusive a versão compilada no Mvn Repository. Isso facilita bastante a criação de um novo projeto baseado em Maven.
Pré-condições para a utilização do f3270
- A execução de um projeto com f3270 exige a utilização do Windows como sistema operacional, devido ao s3270 ser um arquivo executável (ou “.exe”). Este artigo considera a utilização do Windows 10.
- JDK instalado e configurado. Neste artigo, considerarei o OpenJDK 11. Vide link para download e instruções para a instalação para maiores detalhes.
- Maven instalado e configurado. Neste artigo, utilizarei a versão 3.8.3. Vide link para download e instruções para a instalação para maiores detalhes.
- Como IDE para o desenvolvimento da automação com f3270, utilizarei o IntelliJ IDEA. O artigo considerou a versão 2021.1.3 - Community Edition
- Também será necessário o download do wc3270, pacote de programas que contém o s3270. O download pode ser efetuado através deste link. O artigo considera a versão “4.0ga14 stable”.
- Por fim, precisamos de um ambiente para a automação dos testes. Utilizaremos o Sim390, que descrevi no artigo Automação de testes para Mainframe: Ambientes para prova de conceito. Basta seguir as etapas descritas no artigo para a execução do Sim390, e mantê-lo em execução na máquina local.
Configurando um projeto com o f3270
Criarei um novo projeto Maven utilizando o IntelliJ IDEA para a utilização do f3270. Na tela inicial do IntelliJ, clicar em “New Project”:
Selecionar o tipo de projeto “Maven” e clicar em “Next”:
Inserir um nome para o projeto (no caso, darei o nome de “f3270-poc”) e clicar em “Finish”:
Logo após a criação do projeto, será aberto o projeto no IntelliJ IDEA, já com o arquivo “pom.xml” aberto. Será neste arquivo aonde colocaremos as dependências do f3270 e do JUnit (framework que utilizaremos para a implementação de testes). Basta adicionar as seguintes linhas ao arquivo, dentro da tag <project>:
<dependencies>
<!-- f3270 -->
<dependency>
<groupId>com.github.vebqa</groupId>
<artifactId>f3270</artifactId>
<version>1.5.0</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Não esquecer de atualizar as dependências do projeto, clicando no botão indicado abaixo:
Após a configuração das dependências, vamos criar a pasta s3270 na raiz do projeto. Após o download do pacote wc3270 (descrito na seção “Pré-condições para a utilização do f3270”), extrair os arquivos, e copiar o executável “s3270.exe” para dentro da pasta criada no projeto:
Instanciando e conectando a um terminal
Agora, vamos criar uma classe de testes do JUnit5, para a automação de algumas das telas do Sim390. Criarei um package chamado sim390, e dentro deste package criarei a classe Sim390Test:
Dentro desta classe, vamos criar um método de setup, que instanciará um atributo estático, do tipo Terminal. Este método terá a anotação @BeforeAll, para que este método seja executado ao iniciar os testes contidos na classe:
Dentro do método, vamos instanciar o atributo terminal. Olhando para a implementação da classe Terminal, temos os seguintes atributos:
- s3270Path -> caminho para o executável “s3270.exe”.
- hostname -> endereço ip do servidor Mainframe.
- port -> porta para o servidor Mainframe.
- type -> tipo de emulação do Terminal. “TerminalType.TYPE_3278” trará um terminal monocromático, e “TerminalType.TYPE_3279” trará um terminal colorido.
- mode -> modo de emulação, ou a quantidade de linhas e colunas do Terminal. Opções disponíveis: MODE_80_24, MODE_80_32, MODE_80_43 e MODE_132_27.
- charset -> charset do terminal.
- showTerminalWindow -> caso informado “true”, exibirá uma janela com o terminal em execução.
A implementação para acesso ao Sim390 ficou da seguinte maneira:
terminal = new Terminal(
"s3270/s3270",
"127.0.0.1",
23,
TerminalType.TYPE_3279,
TerminalModel.MODE_80_24,
HostCharset.US_INTL,
true);
Por fim, definido o atributo terminal, podemos conectar a ele utilizando o método connect(), ficando a implementação do método setUp da seguinte maneira:
Ações e informações de elementos de tela
O atributo terminal será utilizado também para efetuar ações no terminal, assim como obter informações presentes em tela. Vamos utilizar como exemplo o login, composto de duas telas: a primeira para acessar o login teclando “Enter”, depois a informação de login e senha, seguido novamente da tecla “Enter”.
Vamos implementar uma classe de teste, que executará as ações na tela. Vide na implementação abaixo que os métodos disponíveis pelo atributo terminal são bem intuitivos:
Neste momento, podemos executar os testes e avaliar os primeiros resultados. O primeiro que podemos notar são as telas, que são apresentadas no console da execução do JUnit:
Durante a execução dos testes, é apresentada uma tela para o acompanhamento da execução dos testes. Vide exemplo extraído da tela apresentada após efetuar um login válido:
Algumas das ações disponíveis para utilização: enter, tab, moveCursor, pf, pa, write e clearScreen.
Podemos também extrair informações presentes na tela. Por exemplo, vide abaixo a implementação da extração do challenge exibido na tela de login, através do método getLine:
String challenge = terminal.getLine(6);
System.out.println(challenge.trim());
Os seguintes métodos podem ser utilizados para a extração das informações da tela: getScreenText, getLine, getWidth, getHeight, printFields, printScreen e screenHasLabel.
Elementos de uma tela do Terminal
Cada tela do Terminal é dividida em campos de leitura e escrita. Para demonstrar esta divisão de campos da tela, vamos utilizar o método printFields, para retornar os campos presentes na tela de login:
@Test
public void printFields() throws InterruptedException {
terminal.enter();
terminal.printFields();
Thread.sleep(60000);
}
Após a execução, a informação dos campos existentes na tela é impressa no log de execução do JUnit. Vide extrações do log do JUnit abaixo, com exemplos de campos de leitura e escrita extraídos pelo método printFields:
Os mesmos campos também são listados na janela do terminal, exibida durante a execução de um teste com o f3270. Para esta demonstração, inclui um Thread.sleep (nota do autor: somente para demonstração, sempre evite o Thread.sleep!!!), aonde podemos obter os campos de leitura e escrita:
Localizando elementos na tela
Assim como o Selenium possui localizadores de elementos de tela, o f3270 permite a utilização de localizadores, que podem tanto ser utilizados para a escrita de dados em campos da tela (método write) como para obter dados de campos da tela (método screenHasLabel).
Cada campo pode ser identificado com a utilização da classe FieldIdentifier. Ao instanciar uma variável com a classe, devo informar o texto do campo imediatamente anterior ao campo que quero digitar. Trazendo novamente o exemplo da tela de login:
O identificador do campo pode ser implementado da seguinte maneira:
FieldIdentifier userId = new FieldIdentifier("MUSIC Userid");
Por fim, a escrita no campo é feita com o método write:
terminal.write(userId, "$000");
A implementação do login completo no método de teste fica da seguinte maneira:
O FieldIdentifier também permite com que eu “pule” para o campo que eu quero digitar. Vamos trazer o exemplo da tela a seguir para experimentar esta funcionalidade:
Para digitar a opção no campo , podemos implementar a variável com FieldIdentifier da seguinte maneira:
FieldIdentifier option = new FieldIdentifier("SELECT OPTION", 2);
Escrevendo testes para terminal 5250
Apesar da biblioteca f3270 permitir a automação em terminais 3270, também existe a possibilidade de implementar a mesma automação para terminais 5250, utilizado pela arquitetura AS/400, atualmente conhecida como IBM i. O seguinte artigo da IBM descreve um “de x para” entre as operações do terminal 3270 e 5250: 3270 keyboard mapping for Telnet servers — IBM Documentation.
Na prática, os métodos do f3270 podem ser utilizados para os terminais 5250, com a diferença para as teclas de função. O artigo da IBM descreve o seguinte:
Para exemplificar a utilização do f3270 para terminais 5250, vamos utilizar o servidor pub400.com, cuja configuração expliquei no artigo Automação de testes para Mainframe: Ambientes para prova de conceito. No mesmo projeto de automação, vamos criar um pacote com o nome pub400, contendo a classe Pub400Test:
A implementação, como mencionado anteriormente, é semelhante ao descrito nas seções anteriores, somente com a diferença para o hostname:
Conclusão
Explicamos neste artigo a implementação do f3270 para a automação de testes para sistemas baseados em Mainframe. Olhando com uma maior atenção, temos diversas semelhanças com a implementação de testes Web utilizando Selenium, aonde o Terminal é semelhante a um WebDriver, e o FieldIdentifier tem funcionalidade análoga a um By.
Dadas estas semelhanças, podemos trabalhar a implementação de uma automação utilizando padrões de projeto conhecidos de testes Web, como DriverFactory e PageObjects. No próximo artigo, explicarei a implementação de uma arquitetura prevendo a utilização destes dois padrões.