Automatizando testes de códigos de barras 1D (boletos bancários)

zebra
Recentemente nos projetos em que atuo, tive a necessidade de automatizar um cenário de validação de código de barras de um boleto bancário. Neste post, desejo compartilhar a solução utilizada, o que aprendi e como você pode aplicar isto em seus projetos.

Curiosidades:

Antes de começar, deixe-me te passar algumas informações sobre códigos de barras, que serão úteis posteriormente no processo de automatização:

  • Existem três formatos: 1D (mais comum, produtos por exemplo), 2D(QR_code) e 3D(pouco utilizado, mas por existem algumas propostas conceituais, como o QR Code colorido, sem a cor a 3ª dimensão)
  • Para os códigos de barras 1D, podemos dividir em 1D com padrões de produtos e 1D com padrões industriais- Existem diversos padrões de códigos de barras diferentes para cada uma das dimensões
  • O padrão utilizado pelo boleto bancário aqui no Brasil (grande parte) é o ITF – Intervealed Two of Five
  • No caso do boleto: o valor interpretado pelo código de barras não é o mesmo da linha digitável. Ambos possuem as mesmas informações, mas distribuídas em uma ordem diferente.

Vamos falar sobre a API Zxing

A Zebra Crossing API (Zxing) é uma API para geração e decodificação de códigos de barras 1D/2D. Possui implementação em Java e em outras linguagens como .NET, Javascript, Ruby dentre outras. No meu caso, realizei as implementações utilizando Java.
Abaixo, outras informações úteis sobre a API:

  • Serve para gerar e interpretar códigos de barras de 1D e 2D
  • Aceita diversos modelos (Barcode, QR Code) e diversos padrões
  • Possui o funcionamento semelhante a API/IO do Java- Encontra o código de barras dentro da imagem. Na maioria dos casos, não é necessário uma imagem somente com o código de barras.
  • Possui implementação e wrappers para outras linguagens- Sugestão de validação: ZXing Decoder Online

Validei a consistência de códigos de barras na página web e documento PDF. Abaixo, deixo um passo a passo e alguns exemplos para implementação:
Passo a passo:
1. O primeiro passo é pegar uma imagem do código de barras desejado. Se houver apenas um códigos de barras na tela, a imagem pode ser da tela inteira desde que o código de barras apareça.
2. Utilizando a imagem que salvamos, agora vamos pegar o valor “por extenso”, interpretado, do código de barras.
3. O valor do código de barras precisa ser “transformado” em linha digitável. Logo, vamos fazer isto: transformar o valor do código de barras no padrão de linha digitável.
4. Pegue o valor da linha digitável que será comparada.
5. Com os dois textos “em mãos”, remova os espaçamentos antes, depois e entre os caracteres, assim garantimos que ambos estarão exatamente iguais em tamanho.
6. Compare os dois textos.
7. Shazam! Confira se o resultado está de acordo com o esperado.

Para os passos acima, utilizei duas bibliotecas auxiliares. Preferi descrever em passos os itens acima para que tenhas liberdade para utilizar outras bibliotecas que conheça e que atendam as mesmas necessidades.

Selenium

  • Mapear os elementos
  • Pegar imagem da tela
  • Cortar imagem para pegar apenas o “pedaço” com o código de barras

PDFBox

  • Pegar o conteúdo de um PDF em formato de imagem
  • Pegar o conteúdo do PDF em formato de String

Stella Core

  • Gerar linha digitável a partir do código de barras

Chega de teoria. Vamos aos exemplos! (Java):

1. Pegando imagem

File screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
BufferedImage imagem = ImageIO.read(screenshot);</pre>

Se você prefere uma imagem só do trecho onde o código de barras aparece, pode realizar um corte na imagem, da seguinte forma:

//Pegando a posicao do elemento
Point posicao = codigoDeBarras.getLocation();</pre>
//Pegando altura e largura do elemento

int larguraElemento = codigoDeBarras.getSize().getWidth();int alturaElemento = codigoDeBarras.getSize().getHeight();

//Recortando a imagem

BufferedImage imagemCortada = imagem.getSubImage(point.getX(), point.getY(), eleWidth, eleHeight);
ImageIO.write(imagemCortada, "jpg", imagem);

2. Pegando o valor por extenso, interpretado do código de barras


InputStream codDeBarrasInpStream = new FileInputStream(imagemCortada); 
BufferedImage codDeBarrasBuffImage = ImageIO.read(codDeBarrasInpStream);

//Passa a imagem para uma escala de cinza. Servirá para auxiliar nos passos seguintes. 
LuminanceSource source = new BufferedImageLuminanceSource(codDeBarrasBuffImage);

//O Reader do Zxing identifica e interpreta sequências binárias que estejam de acordo com determinados padrões(ITF, EAN, QR Code, etc)

//O HybridBinarizer serve para passar uma imagem em determinada escalar para uma sequência binária.

//O BynaryBitMap passa a sequência binária para uma sequência compreensível para o Reader. Isso posibilita a decodificação da sequência binária.

BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

//Leitor do código binário gerado anteriormente. É praticamente o passo final para a decodificação!

Reader reader = new MultiFormatReader(); Result result = reader.decode(bitmap); System.out.println("Código de barras interpretado: "+result.getText());

//Se quiser fazer uma validação específica, o result possui um método 
//que retorna qual o formato identificado após a decodificação:

System.out.println("Formato do código de barras: "+result.getFormat());

3. Transformando o valor interpretado em uma linha digitável.


//Utilizei a biblioteca Stella Core da Caelum que já possui um Gerador de linha digitável implementado.

GeradorDeLinhaDigitavel geraLinhaDigitavel = new GeradorDeLinhaDigitavel();

//Passe o banco de acordo com desejado. Neste exemplo, estou usando o Banco do Brasil

String linhaDigitavelGerada = geraLinhaDigitavel.geraLinhaDigitavelPara(codigoDeBarras, Bancos.BANCO_DO_BRASIL.getBanco()); System.out.println("Linha digitável gerada: "+linhaDigitavelGerada);

4. Pegando o valor da linha digitável


//Vou utilizar um exemplo, mas o principal é: encontrar o elemento na tela e pegar seu conteúdo

WebElement elemento = driver.findElement(By.id("linhaDigitavelExemplo"));

String stringLinhaDigitavelTela = elemento.getText(); //ou elemento.getAttribute("value");

System.out.println("Linha digitável na tela: "+stringLinhaDigitavelTela);

5. Removendo espaçamentos para garantir que estejam na mesma formatação


//Neste caso, removi espaços em branco e tabulações.

String linhaDigitavelGerada = linhaDigitavelGerada.replaceAll(" ", "");

stringLinhaDigitavelTela = stringLinhaDigitavelTela.replaceAll(" ", "").replaceAll("\t", "");

6. Comparando os dois textos


boolean estaoIguais = linhaDigitavelGerada.equals("stringLinhaDigitavelTela"); System.out.println("Estão iguais? "+estaoIguais);

7. Conferindo o resultado


//A comparação já nos diz se está OK ou não mas, vale a pena conferir
//Aqui você pode utilizar esses dois resultados em um Assert, por exemplo.

System.out.println("Linha digitavel gerada: "+linhaDigitavelGerada); System.out.println("Linha digitavel boleto: "+stringLinhaDigitavelTela);

Para PDF o processo é praticamente o mesmo. A única coisa que fiz diferente foi a forma como pego a linha digitável: Com a API PDFBox eu passei todo o conteúdo do PDF para String. A partir daí, recortei o trecho onde exibe a linha digitável e utilizei na comparação.


//Passando o caminho do arquivo.

String caminhoArquivo = "/src/test/resources/exemplo/";

File arquivo = new File(caminhoArquivo);

//Realizando a leitura do arquivo

PDF. PDDocument documento = PDDocument.load(pdfFilePath);

//Interpreta o conteúdo do PDF para o formato de texto

String stringPdf = new PDFTextStripper().getText(documento);
//Recortando o trecho onde a linha digitável é exibida

String linhaDigitavelPdf = stringPdf.substring(inicioLinhaDigitavel, fimLinhaDigitavel);

Utilizei também a PDFBox para pegar o conteúdo do PDF como imagem. Com isso, pego a imagem do código de barras no PDF.


//Passando o caminho do arquivo.

String caminhoArquivo = "/src/test/resources/exemplo/";

File arquivo = new File(caminhoArquivo);

//Realizando leitura do arquivo PDF.

PDDocument documento = PDDocument.load(arquivo);

//Instanciando a classe que renderiza o PDF

PDFRenderer renderizador = new PDFRenderer(documento);

//Renderizando uma imagem de um documento PDF. Tira print sempre da última página.

BufferedImage image = renderizador.renderImage(document.getNumberOfPages()-1);

//Escrevendo a imagem em um arquivo

ImageIO.write(image, "JPEG", new File(System.getProperty("user.dir") + "/src/test/resources/exemplo/img/imagem_exemplo.jpg")); 

//fechando o documento após o uso document.close();
document.close()

—-

Espero que com o passo a passo e exemplos você consiga realizar a implementação em seus projetos assim que houver necessidade. Já utilizou este tipo de validação de alguma outra forma? Compartilhe aí! 🙂

Links úteis:

… ps.: Em breve farei uma v2 baseada neste post, explicando como utilizar o Cucumber, seguindo este mesmo projeto como base e exemplo.

… ps².: Vou disponibilizar o projeto de exemplo em algum repositório, mas que você possa utilizar em seus estudos.

… ps³.: Se você chegou até aqui, muito obrigado. Aproveita e deixa teu comentário, se serviu, se já usou algo parecido, se não gostou, fique a vontade e mais uma vez, obrigado!

8 comentários sobre “Automatizando testes de códigos de barras 1D (boletos bancários)

  1. Ótimo tutorial amigo, estou implementando um validador de códigos de barra e estou seguindo seus passos, porém quando eu instancio o objeto da classe BufferedImageLuminanceSource, retorna “cannot be resolved as a type”. Gostaria de saber se ja aconteceu contigo algo similar. Obrigado desde já.

    Curtido por 1 pessoa

    • Olá Kevin! Que legal! Compartilha depois como foi a tua experiência 🙂

      Sobre a sua dúvida, você está tentando criar o objeto da seguinte forma?

      LuminanceSource source = new BufferedImageLuminanceSource(codDeBarrasBuffImage);

      Acho válido verificar se a importação / inclusão da biblioteca no build path funcionaram corretamente.

      A classe LuminanceSource é parte da API zxing, e se estiver com a biblioteca importada, deve funcionar. Recomendo dar uma conferida na documentação desta classe também: https://zxing.github.io/zxing/apidocs/com/google/zxing/LuminanceSource.html

      Acredito que seja isso, espero que ajude! Depois me avisa se deu certo 😉

      Abração

      Curtir

      • Outra dúvida na hora de Inicializar as variaveis inicioLinhaDigitavel e fimLinhaDigitavel, qual o valor que devo iniciar elas. Quando rodo o codigo, caio nessa exception : Gostaria de saber se esse erro pode ser na hora de inicializar as var da linha digitavel. Quando terminar de implementar eu mostro como ficou.
        Exception in thread “main” java.lang.IllegalArgumentException: O código de barras precisa ter 44 digitos
        at br.com.caelum.stella.boleto.bancos.GeradorDeLinhaDigitavel.geraLinhaDigitavelPara(GeradorDeLinhaDigitavel.java:27)
        at ValidadorPDF.ProjetoPDF.Validador.validadorPdf(Validador.java:35)
        at ValidadorPDF.ProjetoPDF.Validador.main(Validador.java:51)

        Curtido por 1 pessoa

      • Oi Kelvin!

        Sobre a primeira dúvida, as variáveis inicioLinhaDigitavel e fimLinhaDigitavel são as posições onde esta informação é exibida no seu arquivo PDF. Uma sugestão é:
        1. Capturar o conteúdo do PDF como String (pode ser com PDFBox)
        2. Exibir no console o conteúdo do PDF como String
        3. Verificar onde inicia e acaba a informação de linha digitável

        Sobre a segunda dúvida/problema:

        Qual o formato da imagem que você está utilizando? O código de barras que você está lendo é de qual padrão/formato?

        Algumas dicas, pra tentar coletar mais informações do problema:
        1. Antes de chamar o gerador de linha digitável, imprime no console o valor que foi interpretado do código de barras. Assim dá pra conferir se está vindo dígitos a mais, menos, ou se está vindo em branco (o que indicaria algum problema)
        2. Realiza um teste com a imagem que você está capturando neste site, pra garantir que sua imagem é “interpretável” pela API Zxing: https://zxing.org/w/decode.jspx – esse é um interpretador online, que eles desenvolveram para demo do produto.

        Curtir

  2. Parabéns pelo tutorial caro amigo automatizador de testes!
    Fiquei muito feliz por ter encontrar tais dicas com facilidade de entendimento e detalhe. No entanto me ocorreu o mesmo problema de nosso camarada Kevin. O código de barras gerado pela leitura da imagem em PDF nunca sai com exatos 44 digitos. Teria alguma ideia do que poderia estar acontecendo?

    Curtido por 1 pessoa

    • Oi Rodrigo!

      Obrigado, espero que o artigo esteja ajudando!

      Sobre a dúvida, se conseguir mais detalhes sobre o boleto / padrão / imagem acho que pode nos ajudar a identificar o motivo.

      Dá uma olhada nas sugestões que dei acima para o Kelvin, acho que vão ajudar. 😉

      Abraço

      Curtir

  3. Olá Samuel,
    Parabéns pelo conteúdo deste post.
    Estou procurando uma aplicação que lê um arquivo PDF (Boleto) e retorna os números do código de barras. Acho que sua solução atende esta necessidade. Correto?
    Você pode disponibilizar um executável? Não sou muito familiarizado com Java….
    Obrigado

    Curtir

Deixe um comentário